this指针与Call/Apply/Bind,new操作符

this指针与Call/Apply/Bind,new操作符

this 指针的指向

  1. 对象属性赋值方法,方法内 this 指向对象本身,
  2. 如果是变量赋值方法,方法内 this 指向 window
  3. (上述可以认为是一个全局定义的函数赋值给变量,map、foreach 内部function 也可以认为是全局定义的function,故也是 window).
  4. 如果将对象属性定义的方法赋值给变量,方法内 this 指向 window.
  5. 变量找不到会去外层找,但是this不会,this找不到就是window.

Call

call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。

举个栗子

csharp 复制代码
function foo () { 
	console.log(this, this.value) 
}
let obj = { value: 2 };
foo.call(obj); // obj, 2

执行步骤可以理解为

  1. 将函数设置为转换的this对象属性(因为对象中运行函数,this指向该对象)
  2. 执行该对象中的函数
  3. 删除对象中该属性
js 复制代码
Function.prototype.myCall = function(ctx, ...args) {
	ctx._callFn = this;
	ctx._callFn(...args);
	delete ctx._callFn;
}
function foo (...args) { 
	console.log(this, this.value, ...args) 
}
let obj = { value: 2 };
foo.myCall(obj, 1, 2, 3); // 2, 1, 2, 3

功能基本完成了,但有些小瑕疵

  1. this 可以为null, 为null时指向window
  2. 既然这个属性时没用的, 如果原目标有_callFn这个属性,就误删了, 用symbol()代替
  3. 函数是可以有返回值的
js 复制代码
Function.prototype.myCall = function (ctx , ...args) {
  const fnCtx = ctx || window;
  const fn = Symbol();
  fnCtx[fn] = this;
  const res = fnCtx.fn(...args);
  delete fnCtx[fn];
  return res;
};
function foo (...args) { 
  return this.value;
}
window.value = 3
let obj = { value: 2 };
console.log(foo.myCall(obj)); // 2
console.log(foo.myCall()); // 3

Apply

apply和Call灰常相似,只是把多个参数,改成参数数组, 只要把...args改成arg即可

js 复制代码
Function.prototype.myApply = function (ctx , argArr) {
  const fnCtx = ctx || window;
  const fn = Symbol();
  fnCtx[fn] = this;
  const res = fnCtx[fn](...argArr);
  delete fnCtx[fn];
  return res;
};
function foo (...args) { 
  console.log(...args);
  return this.value;
}
let obj = { value: 2 };
console.log(foo.myApply(obj, [1, 2])); // 1, 2     2

Bind

MDN 中这样介绍bind:

bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。

bind 其实跟call 很像,唯一区别就是bind返回的是函数, 而call返回的是调用函数后的值

举个栗子

js 复制代码
let obj = { value: 2 };
function foo(...args) { console.log(this.value, ...args) }
foo.bind(obj, 666)(); // 2 666
foo.call(obj, 666); // 2 666

因此,我们可以再call的基础上实现bind

js 复制代码
let obj = { value: 2 };
function foo(...args) { console.log(this.value, ...args) }
Function.prototype.myBind = function(ctx, ...args){
  const preArgs = args;
  return (...args) => this.call(ctx, ...preArgs, ...args); // 箭头函数的this指向外层
}
const bindFn = foo.myBind(obj, 6, 7)
bindFn(8, 9);  // 2, 6, 7, 8 , 9

new操作符

new 运算符允许开发人员创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例

辣么问题来了,如何手写一个new操作符

答案是不行的,因为new操作符是保留字,所以只能用function来模拟一个

new操作符实际进行的工作

  1. 创造一个新对象,新对象的原型指为构造函数的prototype
  2. 执行构造函数内的操作,获取构造函数的返回值
  3. 构造函数有返回值则返回返回值,没有则返回构造对象
js 复制代码
// 自定义new函数
function myNew(fn, ...args) {
  let obj = Object.create(fn.prototype);
  const result = fn.apply(obj, args);
  return result && typeof result === 'object' ? result : obj;  
}

// 测试代码
function Otaku (name, age) {
    this.name = name;
    this.age = age;
    this.habit = 'Games';
}
Otaku.prototype.strength = 60;
Otaku.prototype.sayYourName = function () {
    console.log('I am ' + this.name);
};

var person = myNew(Otaku, 'Kevin', '18');
console.log(person.name); // Kevin
console.log(person.habit); // Games
console.log(person.strength); // 60
person.sayYourName(); // I am Kevin

参考

  1. MDN
  2. JavaScript深入之call和apply的模拟实现
相关推荐
燃先生._.3 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖4 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
black^sugar5 小时前
纯前端实现更新检测
开发语言·前端·javascript
2401_857600956 小时前
SSM 与 Vue 共筑电脑测评系统:精准洞察电脑世界
前端·javascript·vue.js
2401_857600956 小时前
数字时代的医疗挂号变革:SSM+Vue 系统设计与实现之道
前端·javascript·vue.js
GDAL6 小时前
vue入门教程:组件透传 Attributes
前端·javascript·vue.js
小白学大数据6 小时前
如何使用Selenium处理JavaScript动态加载的内容?
大数据·javascript·爬虫·selenium·测试工具
2402_857583497 小时前
基于 SSM 框架的 Vue 电脑测评系统:照亮电脑品质之路
前端·javascript·vue.js
java_heartLake7 小时前
Vue3之性能优化
javascript·vue.js·性能优化