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

1. 提高数组

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

a. 从编译器角度出发 函数

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

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

另外,函数声明会被提高至所在函数做用域顶部,可是函数表达式不会。code

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

可是对象

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

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

b. 函数优先 ip

函数优先于变量进行提高 作用域

 

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);    
    }
}
相关文章
相关标签/搜索