• js概念笔记(this)


    首先要说明的是本文中的this,与mvvm框架如vue,yox等的this是不一样的,本文讨论的this不包括插件中对this变量的封装使用。

    一、this绑定

      this的绑定想必是没有什么疑问的,取决于函数的调用位置。ES6箭头函数的this则取决于函数定义的位置。

    所以想确定this变量究竟指向谁,就要先看函数执行过程中的调用位置或箭头函数的定义位置。不过,记笔记当然不是记那些烂熟于心的东西,是要记那些之前被忽略的知识点。

    1、严格模式与非严格模式下对this变量绑定的影响:

    function foo(){
        "use strict"
        console.log(this.a);  
    }
    
    var a = 2;
    
    foo(); // TypeErr: this is undefined

    严格模式下,全局对象不能用于默认绑定,所以这里this指向的是undefined。然后我们看下面这段代码:

    function foo(){
        console.log(this.a);  
    }
    var a = 2;
    (
    function(){ "use strict"; foo(); //2 })();

    由两段代码的对比可见:在严格模式下调用函数,并不会影响默认绑定。当然这种把严格模式跟非严格模式混着使用在开发中也是不会出现的。

    2、回调函数与setTimeout等内置函数中

    依然是先看一个例子:

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

    这个属于一个隐式绑定的例子,函数调用中的this会被绑定到调用的上下文对象上,既obj。故 this.a 相当于是 obj.a;

    但是如果我们把obj.foo当做参数传入setTimeout中呢?

    function foo(){
        console.log(this.a);
    }
    var obj = {
        a: 2,
        foo: foo
    }
    
    var a = 3;
    
    setTimeout(obj.foo, 1000); //3

    这是因为obj.foo其实是对foo函数的一个引用,在setTimeout中,相当于是:

    function setTimeout(fn, delay){
         //等待delay毫秒
         fn();  //调用位置
    }

    这样看的话,foo函数在调用时并没有上下文,故它使用了默认绑定,绑定到了全局对象上。回调函数的原理也是类似的。这种现象称之为隐式丢失。

    隐式丢失:被隐式绑定的函数丢失了绑定对象,使用默认绑定,从而把this绑定到全局对象或者undefined上。

    3、如何解决隐式丢失——硬绑定:call,apply,bind

    我们用代码来理解什么是硬绑定:

    function foo(){
        console.log(this.a);
    }
    var obj = {
        a: 2
    }
    function bar(){
        foo.call(obj);
    }
    var a = 3;
    setTimeout(bar,200); //2

    上面的例子,不管bar在哪里调用,里面的foo函数的指向都是obj;永远都是obj来调用foo函数。这种强制绑定称之为硬绑定。

    上面的强制绑定,可以用bind来简化下:

    function foo(){
        console.log(this.a);
    }
    var obj = {
        a: 2
    }
    var bar = foo.bind(obj);
    var a = 3;
    setTimeout(bar,200);

    bind(..)会返回一个硬编码的新函数,它会把你指定的参数设置为this的上下文并调用原始函数。

    还有一个常见的对this绑定有影响的写法:使用 new 关键字来调用函数:

    function foo(a){
        this.a = a;
        this.b = 1;
    }
    var a = 2;
    var bar = new foo(3);
    console.log(bar.a); //3

      这里有一个点要强调,在js中,使用new关键字来新建一个对象,跟面向对象语言中使用new初始化类是不一样的。j面向对象语言中使用new初始化类,是调用类中的构造函数。

    js中并没有所谓的构造函数,只有对函数的“构造调用”;

    比如上段代码中,通过 new 关键字创建的bar,console以后的结果,是个普通的对象:{a:2,b:1};通过new关键字创建bar,会自动执行以下操作:

      1、创建(构造)一个全新的对象;

      2、这个新对象会被执行 prototype 连接;

      3、这个新对象会绑定到函数调用的this;

      4、如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。

    接下来我们思考一个问题,如果我先使用强绑定,后使用new关键字,那this究竟会被绑定到哪一个对象上?

    function foo(num){
        this.a = num;
    }
    var obj = {};
    var bar = foo.bind(obj);
    bar(2);
    console.log(obj.a);//2
    var baz = new bar(3);
    console.log(obj.a); //2
    console.log(baz.a); //3

    我们看出,通过 new 关键字 新建的对象baz,baz.a的结果,与obj.a的结果并不相等,说明new修改了foo对于obj的强绑定,new关键字的优先级要高于强绑定。

    以上即为本文对this变量一些容易忽略的细节的讨论,下面,贴出书中总结的一个this指向的判断规则:

      1、函数是否在 new 中调用 ? 如果是,this绑定的是新创建的对象。

      2、函数是否通过 call,apply (显式绑定)或者 bind(强绑定)调用?如果是的话,this绑定的是指定的对象。

      3、函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象;

      4、如果都不是,使用默认绑定,如果在严格模式下,就绑定到undefined,否则绑定到全局对象。

    PS:如果你把null或者undefined作为this的绑定对象传入call、apply、bind,这些值在调用时会被忽略,实际应用的是默认规则;安全的做法是定义一个空对象,传入显式绑定中:

    function foo(a,b){
        console.log("a: "+ a + ", b: " + b);
    }
    var obj = Object.create(null);
    foo.apply(obj,[2,3]);

    以上则为今天分享给大家的与this相关的一些知识点,在看书的过程中竟然找到了使用yox框架多个iframe之间互相调用yox对象中的方法时,this对象被改变无法调用到的问题。

    这里插一句题外话:如果哪位大神知道yox.js框架做国际化的方案,烦请指教一二。ps: 不考虑构建工具的国际化方案;

  • 相关阅读:
    构建工具系列二--Grunt
    构建工具系列一--Travis-cli
    Windows平台下如何使用node.js显示系统盘符
    【译文】采用chrome的DevTool中TimeLine和profile工具提升Web app性能
    浏览器重绘和重排
    迁移oracle数据库至新分区
    模拟app上商品详情点击图片放大并且可以切换大图
    JavaScript 复制内容到剪贴板
    原生js ajax
    原型与原型链
  • 原文地址:https://www.cnblogs.com/solaZhan/p/10926076.html
Copyright © 2020-2023  润新知