Resumen de conocimientos sobre esto, llamar, aplicar, vincular

Conocimiento sobre esto, llamar, aplicar, vincular

Resume los puntos de conocimiento de este, llamar, aplicar, vincular. Los artículos de referencia incluyen los artículos relacionados de Ruan Yifeng y Xian Yu, así como "Programación avanzada de JavaScript". Principalmente un resumen del conocimiento relevante.

contenido:

1. Comprensión de esto

2. Resume la dirección de esto en diferentes ocasiones y los problemas que necesitan atención.

3. Los métodos para vincular esta llamada, vincular, aplicar 

4. Llamada manuscrita

Cinco, aplicar a mano

Seis, encuadernación manuscrita

7. Esto en funciones de flecha

8. Ejercicios sobre los temas señalados por este

1. Comprensión de esto

Artículo de referencia: wangdoc.com/javascript/…

thisEs el objeto donde la propiedad o el método se encuentra "actualmente". Dado que la propiedad o el método de un objeto se puede copiar a otro objeto, el objeto donde la propiedad o el método se encuentra "actualmente" es mutable. Entonces thisel puntero también es variable.

thisEl diseño está relacionado con la estructura de datos en la memoria.

1. Cuando definimos una variable:

var  obj = { name: ‘zz’};

JavaScriptEl motor primero generará un objeto en la memoria {name: ‘zz’}. Luego asigne la dirección de memoria a la variable obj. Entonces, objlo que se almacena arriba es en realidad una dirección. Cuando necesitemos leer  obj.name, primero objobtendremos la dirección y luego namebuscaremos el valor. Todo el mundo debería tener alguna comprensión de la descripción de las variables. La forma que contiene las variables en la memoria es. Lo que leemos [[vaule]]es

{
    name: {
        // 值
        [[value]]: ‘zz’,
        // 是否可写
        [[writable]]: true,
        // 是否可枚举
        [[enumerable]]: true,
        // 是否可配置
        [[configurable]]: true
     }

}
复制代码

2. Cuando definimos una función:

JavaScriptEl motor almacenará la función en la memoria por separado y luego asignará la dirección de la función al nameatributo.value

{

    function f () {
        console.log(this)
    };

    name: {
        // 值
        [[value]]:函数的地址  f,
        …
    }
}
复制代码

Cuando f()se ejecuta solo, hay un entorno de tiempo de ejecución.Cuando se obj.name()ejecuta , objes fel entorno de tiempo de ejecución. Por lo tanto, cuando se referencian otras variables del entorno actual en el cuerpo de la función, los valores obtenidos son diferentes según el entorno en ejecución.

    var  f = function () {
        console.log(this.name);
    }

    var  name = ‘zz’;
   
    var obj = {
        name: ‘dd’,
        f: f
    }

    f() // this指向的是window, this.name zz

    obj.f() // this指向的是obj,this.name dd
复制代码

由此才会有那句:函数(非箭头函数)中的this 永远指向最后调用它的那个对象。

二、总结this在不同场合的指向

1、全局环境: 执向顶层对象  window

function f() {
    console.log( this === window);
}

f();
复制代码

2、构造函数: this指向的实例对象

var  Persion = function (name) {
    this.name = name;
}

var p = new Persion(‘zz’);
p.name // zz
复制代码

3、对象的方法

如果对象的方法里面包含thisthis的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变this的指向。

var obj ={
    foo: function () {
        console.log(this);
    }
};

obj.foo() // obj

// 情况一
(obj.foo = obj.foo)() // window

// 情况二
(false || obj.foo)() // window

// 情况三
(1, obj.foo)() // window
复制代码

解析:

JavaScript 引擎内部,objobj.foo储存在两个内存地址,称为地址一和地址二。obj.foo()这样调用时,是从地址一调用地址二,因此地址二的运行环境是地址一,this指向obj

但是,上面三种情况,都是直接取出地址二,赋值后进行调用,这样的话,运行环境就是全局环境,因此this指向全局环境。

// 情况一

(obj.foo = function () {
    console.log(this);
})()

// 等同于

(function () {
    console.log(this);
})()
复制代码
// 情况二
(false || function () {
    console.log(this);
})()

// 情况三

(1, function () {
    console.log(this);
})()
复制代码

如果this所在的方法不在对象的第一层,这时this只是指向当前一层的对象,而不会继承更上面的层。


var a = {
    p: 'Hello',
    b: {
        m: function() {
          console.log(this.p);
        }
    }
 };

a.b.m() // undefined
复制代码

4、遇到的问题

(1)避免多层嵌套, 如果遇到嵌套,需要在上层使用一个变量固定this,然后再在内层函数调用这个变量。

var o = {
    f1: function() {
        console.log(this);
        
        var that = this;
        
        var f2 = function() {
          console.log(that);
        }();
    }
}

o.f1()

复制代码

(2)避免数组处理方法中的this。 比如foreach, map的回调函数中 this是指向了window的,如果需要固定this,可以采用 在上层定义变量的方式,或者在foreach的第二个参数绑定this的值

var o = {
    v: 'hello',
    p: [ 'a1', 'a2' ],
    f: function f() {
        var that = this;
        this.p.forEach(function (item) {
            console.log(that.v+' '+item);
        });
     }
    
    //  另一种方法
    //this.p.forEach(function (item) {
    //    console.log(this.v + ' ' + item);
    //}, this);
}

o.f()

复制代码

三、绑定this的方法call,bind, apply

1、Function.prototype.call()

call 方法可以指定函数内部this的指向(函数执行时所在的作用域)

call 方法可以接受多个参数

func.call(thisValue, arg1, arg2, …)

第一个参数:this所要指向的那个对象。这里的thisValue 是一个对象,

(1)如果参数为空,null 和 undefined 默认为全局对象

(2)如果this是一个原始值,就会转换为对应的包装对象

(3)如果是正常对象,就不用做转换了

var  n = 100;
var obj = {n: 200};

function a () {
    console.log(this.n);
}

function f () {
    return this;
}

a.call() // 100
a.call(null) // 100
a.call(undefined) // 100
a.call(obj) // 200;
f.call(5) // // Number {[[PrimitiveValue]]: 5}
复制代码

其他的参数:是函数调用时所需的参数

function add (a, b) {
    return a + b;
}

add.call(this, 1, 2) // 3

复制代码

2、Function.prototype.apply()

用法和call是类似的,区别是 接受一个数组作为函数执行时的参数。

func.apply(thisValue, [arg1, arg2,…])

3、Function.prototype.bind()

(1) bind() 将函数体内的this绑定到某个对象,然后返回一个新函数。

bind的第一个参数 是函数体内this绑定的某个对象。如果第一个参数是null,或者 undefined,则默认将this绑定到全局对象中。

var  counter = {
    count: 0,
    inc: function () {
        this.count++;
    }
}

const func = counter.inc.bind(counter);

func() // 1
复制代码

(2)bind还可以接受更多的参数

var add = function (x, y, z) {
    return x * this.m + y * this.n + z;

}

var obj = {
    m: 2,
    n: 2
};


var newAdd = add.bind(obj, 5);
const result = newAdd(5, 6);
console.log('result----', result);// 26


 var newAdd = add.bind(obj, 5, 5);
 const result = newAdd(6);
 console.log('result----', result);// 26

// 这里 bind后面的参数,是给add绑定强两个参数,之后每次调用 newAdd 就只用传入第三个参数。
复制代码

(3)bind方法的注意点:每一次返回都是一个新的函数

这种方式就无法取消这个匿名函数

element.addEventListener('click', o.m.bind(o));
element.removeEventListener('click', o.m.bind(o));
复制代码

     

正确方法:


var listener = o.m.bind(o);
element.addEventListener('click', listener);
element.removeEventListener('click', listener);
复制代码

四、手写call方法

参考文章:segmentfault.com/a/119000000…

call方法的作用和参数

(1)call改变了this的指向

(2)call绑定的方法执行了

(3)call 可以携带参数

模拟的步骤

(1) 将函数设为绑定对象(可以为null,undefined)的属性,

(2)执行这个函数,并传递相关的参数

(3)删除这个函数

Function.prototype.call2 = function(obj) {
    const context = obj || window;
    context._func = this;

    let args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push(arguments[i]);
    }

    const result = context._func(...args);
    delete context._func;
    return result;
}
 
var value = 2;

var obj = {
    value: 1
}

function bar(name, age) {
    console.log(this.value);
    return {
        value: this.value,
        name: name,
        age: age
    }
}

bar.call2(null); // 2

console.log(bar.call2(obj, 'kevin', 18));

/*
    1,
    {value: 1, name: kevin; 18}
*/

复制代码

五、手写apply  原理和call是样的。只在参数部分有区别

Function.prototype.apply2 = function (obj, arr) {
    const context = obj || window;
    context._fn = this;

    var result;
    if (!arr || !arr.length) {
        result = context._fn();
    }
    else {
        result = context._fn(...arr);
    }
    delete context._fn
    return result;

 }

复制代码

六、手写bind

参考文章:github.com/mqyqingfeng…

总结下bindbind() 方法会创建一个新的函数,新函数被调用时,bind的第一个参数将作为它运行时的this之后的参数,将作为函数的参数传入。

bind的特点: 返回一个函数, 第一个参数是新函数的运行环境 ,可以传入参数

(1)关于this的绑定部分,可以使用 call, apply来实现。

Function.prototype.bind2 = function (context) {
    var self = this;
    return function () {
        return self.apply(context);
    }
}
复制代码

(2) 传递参数

var foo = {
    value: 1
};

function bar(name, age) {
    console.log(this.value);
    console.log(name);
    console.log(age);
}

var bindFoo = bar.bind(foo, 'daisy');
bindFoo('18');

复制代码

通过上面的例子,我们知道 bindFoo的参数包含两次传递的参数


Function.prototype.bind2 = function (context) {
    var self = this;
    // 获取bind2函数从第二个参数到最后一个参数。 bind调用时传递的参数
    var args = Array.prototype.slice.call(arguments, 1);

    return function () {
        // 这个时候的arguments是指bind返回的函数传入的参数
        var bindArgs = Array.prototype.slice.call(arguments);
        return self.apply(context, args.concat(bindArgs));
    }
}
复制代码

(3)bind返回的新函数,可以作为构造函数,此时bind时指定的this值会失效,但是传入的参数值时有效的。

Function.prototype.bind2 = function (context) {
    //  调用bind的必须是一个函数
    if (typeof this !== "function") {
        throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);


    // 做原型链继承的中转
    var fNOP = function () {};

    var fBound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
       //  如果是用的是new 调用的 bind返回的函数,判断此时this的指向。
        return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));

    }

    // 修改fBound的.prototype

    fNOP.prototype = this.prototype;

    fBound.prototype = new fNOP();

    return fBound;

}

复制代码

以上关于,call, apply, bind的代码手写来自于 冴羽 的文章,好文值得分享。在这里做记录标记了自己的理解

七、箭头函数中的this

参考文章:juejin.cn/post/694602…

创建箭头函数时,就已经确定了它的this指向。 箭头函数内的this指向外层的this

所以要想确定箭头函数this,就必须要先知道外层this的指向。 参考上文关于this指向的总结

八、练习题

参考文章:

juejin.cn/post/695904…

juejin.cn/post/706939…

在实际的题目中,会存在各种情况。要把握住的是:

this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件

this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式

关于调用的方式:确定函数的调用位置和调用栈,确定this的绑定对象。

第一题

    function foo() {
        console.log(this)
        console.log( this.a );
    }

    function doFoo() {
        console.log(this)
        foo();
    }

    var obj = {
      a: 1,
      doFoo: doFoo
    };

    var a = 2; 
    obj.doFoo()

复制代码

正确输出:

/*
    obj:{a:1. doFoo:}
    window
    2
    obj.doFoo 函数的 的运行环境是 obj
    obj.doFoo() 内部调用的是 foo() 此时 foo 调用位置是在 doFoo中。但是foo的this绑定是默认的window
    foo() 输出是 2
*/
复制代码

第二题

    var a = 10
    var obj = {
        a: 20,
        say: () => {
            console.log(this.a)
        }
    }

    obj.say() 

    var anotherObj = { a: 30 } 
    obj.say.apply(anotherObj) 
复制代码

正确输出

/*
      obj.say() 是箭头函数,内部的是this 此时为上一次obj的this指向 是window, 输出 10
      obj.say.apply(anotherObj) 此时把 obj.say的运行环境改为了anotherObj, this对anotherObj指向的 window 输出:10
    */
复制代码

第三题

function a() {
  console.log(this);
}

a.call(null);
复制代码

正确输出

 window
 call的第一个参数为null时,指向的是window
复制代码

第四题

var obj = { 
  name : 'cuggz', 
  fun : function() { 
    console.log(this.name); 
  } 
} 
obj.fun()   
new obj.fun()
复制代码

正确输出

 /*
      obj.fun() 此时fun 函数的运行环境是 obj, 此时的this指向为obj,this.name 输出: cuggz
      new obj.fun() 是取出了 obj.fun 作为构造函数,此时的this指向的是构造函数, 因为没有name 参数,输出:undefined
    */
复制代码

第五题

var obj = {
  say: function() {
    // var f1 = () =>  {
    //   console.log("1111", this);
    // }
    // f1();
    const f1 = function() {
        console.log("1111", this);
    }
    f1();
  },
  pro: {
    getPro:() =>  {
        console.log(this);
    }
  }
}
var o = obj.say;
o();
obj.say();
obj.pro.getPro();
复制代码

正确的输出

/*
      var o = obj.say; 是把say函数赋值给了 o 此时 o是在全局作用域中;o()  输出: 1111 window
      obj.say() 此时say的运行环境是 obj, f1 是箭头函数,指向外层say所在的运行环境 所以此时  输出: 1111 obj
      如果f1不是箭头函数,那么f1 的运行环境就是 window。f1虽然在 say函数内调用,但是默认的绑定是 window
      obj.pro.getPro() getPro是箭头函数,this指向外层的this,getPro的外城 pro是个对象不够成单独的作用域,所以指向了obj的运行环境window。 输出  window

    */
复制代码

第六题

    var myObject = {
        foo: "bar",
        func: function() {
            var self = this;
            console.log(this.foo);  
            console.log(self.foo);  
            (function() {
                console.log(this);
                console.log(this.foo);  
                console.log(self.foo);  
            }());
            // var fn = function() {
            //     console.log(this);
            //     console.log(this.foo);  
            //     console.log(self.foo);  
            // };

            // fn();
        }
    };

    myObject.func();
复制代码

正在输出

   /*
        myObject.func()
        this.foo  输出  bar
        self.foo 此时 this和self的指向是相同的  输出 bar

        立即指向函数中function中 this 指向的是window, 此时函数没有绑定到func上。 输出:undefined
        self指向的是 func, 此时的 self.foo 输出 bar
    */
复制代码

第七题

  window.number = 2;

var obj = {
    number: 3,
    db1: (function test1() {
       console.log('ddd', this);
       this.number *= 4;
       return function test() {
         console.log('return', this);
         this.number *= 5;
       }
     })()
}

var db1 = obj.db1;
db1();

obj.db1();
console.log(obj.number);     
console.log(window.number); 
复制代码

正确输出

/*
        obj.db1 是个立即执行函数,在解析时就立即执行了。会输出 ddd window,此时 this.number = 2 * 4 = 8;
        var db1 = obj.db1; db1 指向的是 obj.db1对应的立即执行函数的返回值. 此时 return 指向的是 window
        obj.db1() 调用的实际是 obj.db1的返回函数, return obj, this.number = 15
        obj.number 的输出就是 15
        window.number 的输出是 8   
    */
复制代码

第八题

var length = 10;

function fn() {
    console.log(this.length);
}
    
var obj = {
    length: 5,
    method: function(fn) {
        fn();
        arguments[0]();
    }
};
    
obj.method(fn, 1);
复制代码

salida normal

  /*
        obj.method(fn, 1); fn是传入的函数,this 是window,输出是 10
        arguments[0] => function fn() {console.log(this.length);}
        此时 this 是 [ƒ, 1, callee: ƒ, Symbol(Symbol.iterator): ƒ] arguments,
        所以运行环境是arguments, 输出是 2
    */
复制代码

Pregunta 9

    var a = 1;
    function printA() {
        console.log(this.a);
    }
    var obj = {
        a: 2,
        foo: printA,
        bar: function() {
            printA();
        }
    }

    obj.foo(); 
    obj.bar(); 
    var foo = obj.foo;
    foo(); 
   
复制代码

salida correcta

/*
        obj.foo 存入的是 printA函数的指针。obj.foo() 此时printA的运行环境是 obj  this.a 输出 2
        obj.bar() 函数内部的 printA() 运行环境是 window  输出 1
        foo = obj.foo 此时把 printA 函数的指针 赋值给了 foo。 运行环境是 window。 foo() 输出 1
 */
复制代码

Pregunta 10

    var x = 3;
    var y = 4;
    var obj = {
        x: 1,
        y: 6,
        getX: function() {
            var x = 5;
            return function() {
                return this.x;
            }();
        },
        getY: function() {
            var y = 7;
            return this.y;
        }
    }
    console.log(obj.getX()) 
    console.log(obj.getY()) 
复制代码

salida correcta

  /*
        obj.getX() // function() { return this.x;}(); // 立即执行函数 运行环境是 window 对应的function 输出: 3
        obj.getY() //  运行环境是 obj  输出:6
    */
复制代码

Pregunta 11

var a = 10; 
var obt = { 
    a: 20, 
    fn: function() { 
        var a = 30; 
        console.log(this.a)
    } 
}
obt.fn();  
obt.fn.call();
(obt.fn)();
复制代码

salida correcta

/*
        obt.fn() 运行环境是 obt this.a 输出 20
        obt.fn.cal() 绑定了全局环境 this.a 输出 10
        (obt.fn) 是全局环境, this.a 输出 10
    */
复制代码

Pregunta 12

  function a(xx) {
        this.x = xx;
        return this
    };
    var x = a(5);
    
    var y = a(6);

    console.log(x.x)  
    console.log(y.x)
复制代码

salida correcta

  /*
        a(5) 返回的 this 是window; x = 5; x 覆盖了之前x  输出:undefined
        a(6) y = {x: 6}  输出:6
    */
复制代码

Pregunta 13

  function foo(something) {
        this.a = something
    }

    var obj1 = {
        foo: foo
    }

    var obj2 = {}

    obj1.foo(2); 
    console.log(obj1.a); 

    obj1.foo.call(obj2, 3);
    console.log(obj2.a);

    var bar = new obj1.foo(4)
    console.log(obj1.a); 
    console.log(bar.a); 
复制代码

salida correcta

    /*
        obj1.foo(2) 输出 obj1.a // 2  此时 obj1上添加了一个属性 a 值为2
        obj1.foo.call(obj2, 3)  obj2.a // 3

        obj1.a // 2
        bar.a // 4
    */
复制代码

Pregunta 14

    function foo(something){
        this.a = something
    }

    var obj1 = {}

    var bar = foo.bind(obj1);
    bar(2);
    console.log(obj1.a); 

    var baz = new bar(3);
    console.log(obj1.a); 
    console.log(baz.a); 
复制代码

salida correcta

 /*
        foo.bind(obj1) 是吧foo中的this替换为obj1, 返回了bar
        bar(2) 给foo赋值2  obj1.a // 2

        new bar(3) 构造函数,此时 foo.bind指定的this指向obj1失效。
        obj1.a // 2
        baz.a // 构造函数中的this.a  3
    */
复制代码

Supongo que te gusta

Origin juejin.im/post/7079635729227186183
Recomendado
Clasificación