大数跨境
0
0

【前端知识】详细的谈一谈apply, call, bind

【前端知识】详细的谈一谈apply, call, bind 元讯趣编程
2022-04-20
1
导读:【前端知识】详细的谈一谈apply, call, bind

微信公众号:趣编程ACE
关注可了解.NET日常开发技巧。如需源码,请公众号留言 源码;


01 前言

这又是一个面试经典问题,也是 ES5 中众多坑中的一个。虽然在 ES6 中可能会极大避免 this 产生的错误,但是为了一些老代码的维护,详细的了解一下 this 的指向和 call、apply、bind 三者的区别还是很有必要的。

02 this的指向

了解call,apply,bind之前我们先来了解下js中 this 的指向。其实 this 的指向,始终坚持一个原理: this 永远指向最后调用它的那个对象。

下面我们就来讨论下这些场隐式绑定的场景this指向:

  • 全局上下文

  • 直接调用函数

  • 对象.方法的形式调用

  • DOM事件绑定(特殊)

  • new构造函数绑定

  • 箭头函数

全局上下文

全局上下文默认this指向window, 严格模式下指向undefined。

直接调用函数

let obj = {
a: function() {
console.log(this);
}
}
let func = obj.a;
func();

这种直接调用函数的情况。this相当于全局上下文的情况,默认指向window。

对象.方法 调用

let obj = {
a: function() {
console.log(this);
}
}
obj.a();

这种对象.方法的调用情况。this指向的是这个对象。

DOM事件绑定

<div></div>
<button onclick="myClick()">123</button>
let dom = document.querySelector('div');
dom.addEventerListener('click',function(){
console.log(this);
});

function myClick(){
console.log(this);
}
  • addEventerListener中 this 默认指向绑定事件的元素。

  • onclick等系列事件和IE中使用的attachEvent默认this指向window。

new构造函数

function Person(name) {
this.name = name;
this.age = 6;
}
let tz = new Person('Tz');

此时构造函数中的this指向实例对象(tz)。

箭头函数

箭头函数没有this, 因此也不能绑定。里面的this会指向当前最近的非箭头函数的this,找不到就是window(严格模式是undefined)。

let obj = {
a: function() {
let do = () => {
console.log(this);
}
do();
}
}
obj.a();

this找到最近的非箭头函数是a方法,a现在绑定着obj, 因此箭头函数中的this是obj。

03 apply,call,bind

bind,call,apply都是用于显性的绑定this,从而改变this的指向。

apply

MDN给出的定义是:

apply() 方法调用一个函数, 其具有一个指定的this值,以及作为 一个数组(或类似数组的对象) 提供的参数

语法:

func.apply(thisArg, [argsArray])

  • thisArg 必选。在 func 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。

  • argsArray 可选。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 null 或 undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。

function Product(name, price) {
this.name = name;
this.price = price;
}

function Food(arr) {
Product.apply(this, arr); //this指向实例对象food
this.category = 'food';
}

let food = new Food(['cheese', 5])

call

MDN给出的定义是:

call() 方法使用一个指定的 this 值和单独给出的 一个或多个参数 来调用一个函数。

语法:

function.call(thisArg, arg1, arg2, …)

  • thisArg 可选。在 function 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。

  • arg1, arg2, ... 指定的参数列表。

function Product(name, price) {
this.name = name;
this.price = price;
}

function Food(name, price) {
Product.call(this, name, price); //this指向实例对象food
this.category = 'food';
}

let food = new Food('cheese', 5);

bind

MDN给出的定义是:

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

语法:

function.bind(thisArg[, arg1[, arg2[, …]]])

  • thisArg 调用绑定函数时作为 this 参数传递给目标函数的值。如果使用new运算符构造绑定函数,则忽略该值。当使用 bind 在 setTimeout 中创建一个函数(作为回调提供)时,作为 thisArg 传递的任何原始值都将转换为 object。如果 bind 函数的参数列表为空,或者thisArg是null或undefined,执行作用域的 this 将被视为新函数的 thisArg。

  • arg1, arg2, ... 当目标函数被调用时,被预置入绑定函数的参数列表中的参数。

function Product(name, price) {
this.name = name;
this.price = price;
}

function Food(name, price) {
Product.bind(this, name, price)(); //this指向实例对象food
this.category = 'food';
}

let food = new Food('cheese', 5);

三者区别

  • apply 使用 数组 作为参数;

  • call 使用 参数列表 作为参数;

  • bind 使用 参数列表 作为参数,且创建了一个新的函数,需要 被调用 才会起作用。

04 手写模拟实现apply方法

Function.prototype.myapply = function (thisArg, args) {
thisArg = Object(thisArg) || window;
let fn = Symbol();
thisArg[fn] = this;
let result = thisArg[fn](...args);

delete thisArg[fn];
return result;
}

//test
let obj = {
a: 1
}

function test(x1, x2) {
console.log(x1, x2) //2, 3
console.log(this.a) //1
}
test.myapply(obj, [2, 3])

05 手写模拟实现call方法

Function.prototype.mycall = function (thisArg, ...args) {
thisArg = Object(thisArg) || window;
let fn = Symbol();
thisArg[fn] = this;
let result = thisArg[fn](...args);

delete thisArg[fn];
return result;
}

//test
let obj = {
a: 1
}

function test(x1, x2) {
console.log(x1, x2) //2, 3
console.log(this.a) //1
}
test.mycall(obj, 2, 3)

06 手写模拟实现bind方法

Function.prototype.mybind = function(thisArg, ...args){
return (...newArgs) => {
return this.call(thisArg, ...args, ...newArgs)
}
}

// test
let obj = {
a: 1
}

function test(x1, x2) {
console.log(x1, x2) //2, 3
console.log(this.a) //1
}


【声明】内容源于网络
0
0
元讯趣编程
学习编程从入门到精通
内容 51
粉丝 0
元讯趣编程 学习编程从入门到精通
总阅读71
粉丝0
内容51