首先是几条规律:
- 简单调用函数时候,严格模式是undefined,非严格模式会绑定到
globalThis
上 - new调用构造函数,构造函数内的this会被绑定到新创建的对象上
- 通过
call/apply/bind
显示函数时候,this会绑定到指定参数的对象上 - 通过上下文对象调用,this会绑定到该对象上
- 箭头函数,this由外层作用域来决定
new
new做了什么?
this优先级
显式绑定 apply/call/bind/new,魔法互喷,哪个优先级更高?
function foo(a) {
console.log(this.a);
}
const obj1 = {
a: 1,
foo: foo,
};
const obj2 = {
a: 2,
foo: foo,
};
obj1.foo.call(obj2); // 2
obj2.foo.call(obj1); // 1
答案是,显式绑定优先级更高。既然知道了答案,可以继续看
function foo(a) {
this.a = a;
}
const obj1 = {};
var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); //2
- bar 是foo的函数,但修改了this为obj1,通过隐式调用不会修改this指向
- 调用foo函数,传参2,this是obj1,因此 obj1.a=2
var baz = new bar(3)
log(baz.a) // 3
bar本身已经改变了this指向,现在通过new来调用,返回了新实例 baz, this也指向baz ,此时 baz.a=3
结论是: new优先级比 bind高,可以覆盖掉
function foo() {
return (a) => {
console.log(this.a);
};
}
const obj1 = {
a: 2,
};
const obj2 = {
a: 3,
};
const bar = foo.call(obj1);
console.log(bar.call(obj2)); // 2
- bar 是foo函数,内部的this指向了obj1,因此this.a=2
- 后续 把 bar里的this指向obj2 但是并不生效
- 如果把foo里的箭头函数改掉,log会打印3
实现bind
来实现一个bind吧。sad
一个基础版本
Function.prototype.bind =
Function.prototype.bind ||
function (ctx) {
var that = this;
var args = [...arguments].slice(1);
return function bound() {
var innerArgs = [...arguments];
var finalArgs = args.concat(innerArgs);
return that.apply(ctx, finalArgs);
};
};
这里实现了bind,返回的函数,内部拼接了参数,使用了apply传递数组
但,我们已知 new的优先级会高于bind,因此,这里需要对来源,也就是调用方式进行判断
也就是在
手写一个
call
Function.prototype.myCall = function(ctx) {
if (typeof this !== "function") {
throw new TypeError("error");
}
ctx = ctx || window;// 不传参数就是 window
ctx.fn = this; // 创建fn
const args = [...arguments].slice(1);// 多参数
const result = ctx.fn(...args);
delete ctx.fn;
return result;
};
apply
Function.prototype.myApply = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window
context.fn = this
let result
// 处理参数和 call 有区别
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
bind
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
const _this = this
const args = [...arguments].slice(1)
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以 new F(),所以需要判断
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}
new
自己写一个new?
- 新生成对象
- 链接到原型
- 绑定this
- 返回新对象
functon create(){
let obj={} // 1 新对象
let Con=[].shift.call(arguments) // 获取传入参数,构造函数
obj.__proto__=Con.prototype // 绑定空对象原型
let result = Con.apply(obj,arguments) //绑定this并执行构造函数
return result instanceof Object?result:obj
}
instanceof
自己实现一个 instanceof
function myInstanceof(left,right){
let prototype=right.prototype
left = left.__proto__
while(true){
if(left===null || left===undefined)
return false
if(prototype===left) return true
left=left.__proto__
}
}
获取右边的原型。获取左边的原型。循环判断原型,直到null
Function.prototype.myCall = function(ctx) {
if (typeof this !== "function") {
throw new TypeError("error");
}
ctx = ctx || window;// 不传参数就是 window
ctx.fn = this; // 报存this
const args = [...arguments].slice(1);// 多参数
const result = ctx.fn(...args);
delete ctx.fn;
return result;
};
-1 参考链接
- 《前端开发核心知识进阶》第一部分