• 你不知道的Javascript(上卷)读书笔记之四 ---- 提升、this


    1. 提升

    使用var声明的变量声明和函数的声明(函数表达式不会)会被提升至所在函数作用域顶部

    a. 从编译器角度出发

    回忆一下, 中关于编译器的内容,引擎会在解释 JavaScript 代码之前首先对其进行编译。

    编译阶段中的一部分工作就是找到所有的声明, 并用合适的作用域将它们关联起来。值得注意的是, 每个作用域都会进行提升操作。

    另外,函数声明会被提升至所在函数作用域顶部,但是函数表达式不会。

    foo(); //没有错误
    function foo() {
    }

    但是

    foo(); // TypeError
    bar(); // ReferenceError
    var foo = function bar() {};

    在这里foo进行了变量声明,进行了变量提升,但是对它进行调用会抛出TypeError错误,bar是一个函数表达式,不会进行提升

    b. 函数优先

    函数优先于变量进行提升

     

    2. this

    如果是在Java中,那么this其实没有什么理解的难度,this指向类对象本身。但是在Javascript中,每一个函数都会隐式地传递一个this,并且根据函数调用的不同情况,this会有不同的指向。每个函数的this都是在调用时被绑定的,this的指向完全取决于函数的调用位置。

    a.调用位置

    在理解this之前,首先必须要理解什么是调用位置,this完全依靠调用位置(而不是声明位置)来绑定this的指向,相同的函数被不同区域的调用会导致this指向 的不同。

    b.绑定位置

    this的绑定可以理解为以下四种调用规则:

            b.1 默认绑定 ---- 适用于独立函数调用:

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

    在独立函数调用时,函数调用应用了this的默认绑定,this指向全局作用域

    在严格模式下,无法使用默认绑定,this只会指向undefined;

            b.2 隐式绑定 ---- 调用时存在上下文对象

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

    调用位置会使用obj上下文来调用函数,隐式绑定规则会把函数调用中的this 绑定到上下文对象,在隐式绑定规则下this会指向obj。

    但是,隐式绑定存在着隐藏的风险:隐式丢失

    隐式绑定的函数会丢失绑定的对象(或者可以称为丢失了函数绑定的上下文对象),它会使用默认绑定原则,绑定到undefined或者全局作用域上,

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

    虽然bar()引用的是obj中foo()函数,但是函数在调用的时候 并没有传入一个上下文对象,在这种情况下调用,就跟调用普通的独立函数没有区别

            b.3 显式绑定

    存在一些情况,我们想要往一个函数中显式地绑定this,我们可以使用call(..) 和apply(...)函数

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

            b.4 new绑定

    不同于传统的面向对象语言,Js中的构造函数是特殊的函数,虽然都是使用 new 进行调用,但是在Js中使用new的机制和传统面向对象语言完全不同。

    在使用new来调用构造函数时,会执行以下步骤:

    First.创建一个全新的对象。

    Second.这个对象会被执行[原型]连接

    Third.新对象将会绑定到函数调用的this

    Fourth.如果函数没有返回其他对象,那么new 表达式中的函数将会自动返回 这个新对象

    c.四种绑定方式的优先级

    首先优先级最低的是默认绑定,当其他绑定规则都不生效时使用默认绑定

    //c.1 显式绑定 > 隐式绑定
    obj.foo.call(obj2) // 传入的this绑定的对象是obj2
    //c.2 new 绑定 > 隐式绑定
    new obj.foo(); // this绑定的是新创建的对象
    //c.2 new 绑定 > 显式绑定
    function foo(p1, p2) {
        this.val = p1 + p2;
    }
     
    var bar = foo.bind(null, “p1”);
    var baz = new bar(“p2”);
    baz.val; // p1p2

    总结一下就是: new > 显式 > 隐式 > 默认

    d. 绑定例外

    例外情况一:把null, undefined 作为this的绑定对象传入call/apply/bind时会使用默认绑定规则

    在什么时候下会传入null?

    做法一:使用apply(..)来展开一个数组
    function foo(a, b){
        console.log(“a:” + a + “, b:” + b);
    }
    foo.apply(null, [2, 3]);
    做法二:对参数进行”柯里化”(预先设置参数)
    var bar = foo.bind(null, 2);
    bar(3);

    在ES6中,可以用...操作符代替apply(..)来展开数组,比如foo(...[1,2])

    例外情况二:一个函数的间接引用,这个时候会使用默认绑定

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

    例外情况三:ES6新出现的箭头函数

    ES6中新增加了一种无法使用之前四种规则的特殊函数类型:箭头函数

    箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定 this

    function foo() {
        return () => {
            console.log(this.a);
        }
    }
     
    var obj1 = {a:2};
    var obj2 = {a:3};
    var bar = foo.call(obj1);
    bar.call(obj2); // 2

    foo内部的箭头函数会捕获调用foo()时候的this,一经绑定无法修改。

    实际上它们的效果就与ES6之前的这种写法差不多:

    function foo() {
        var self = this;
        return function(){
            console.log(self.a);    
        }
    }
  • 相关阅读:
    第四章:文件stat获取函数
    第四章:文件的访问权限
    第三章:ioctl 函数详解
    第三章:fcntl 函数详解
    第四章:用户ID和组ID
    第四章:文件属性更改
    第三章:文件 I/O
    第四章:文件类型
    第二章:Unix的标准化及实现
    xml DOM解析
  • 原文地址:https://www.cnblogs.com/butter-fly/p/6343500.html
Copyright © 2020-2023  润新知