首先是几条规律:
- 简单调用函数时候,严格模式是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); // 2obj2.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;// 不传参数就是 windowctx.fn = this; // 创建fnconst 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 || windowcontext.fn = thislet result// 处理参数和 call 有区别if (arguments[1]) {result = context.fn(...arguments[1])} else {result = context.fn()}delete context.fnreturn result}
bind
Function.prototype.myBind = function (context) {if (typeof this !== 'function') {throw new TypeError('Error')}const _this = thisconst 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.prototypeleft = left.__proto__while(true){if(left===null || left===undefined)return falseif(prototype===left) return trueleft=left.__proto__}}
获取右边的原型。获取左边的原型。循环判断原型,直到null
Function.prototype.myCall = function(ctx) {if (typeof this !== "function") {throw new TypeError("error");}ctx = ctx || window;// 不传参数就是 windowctx.fn = this; // 报存thisconst args = [...arguments].slice(1);// 多参数const result = ctx.fn(...args);delete ctx.fn;return result;};
-1 参考链接
- 《前端开发核心知识进阶》第一部分
