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);
}
}

你不知道的Javascript(上卷)读书笔记之四 ---- 提升、this的更多相关文章

  1. JavaScript词法作用域—你不知道的JavaScript上卷读书笔记(一)

    前段时间在每天往返的地铁上抽空将 <你不知道的JavaScript(上卷)>读了一遍,这本书很多部分写的很是精妙,对于接触前端时间不太久的人来说,就好像是叩开了JavaScript的另一扇 ...

  2. JS闭包—你不知道的JavaScript上卷读书笔记(二)

    关于闭包,初学者会被绕的晕头转向,在学习的路上也付出了很多精力来理解. 让我们一起来揭开闭包神秘的面纱. 闭包晦涩的定义 看过很多关于闭包的定义,很多讲的云里雾里,晦涩难懂.让不少人以为闭包是多么玄乎 ...

  3. 你不知道的javascript 上卷 读书笔记

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. JavaScript中的对象与原型—你不知道的JavaScript上卷读书笔记(四)

    一.对象 对象可以通过两种形式定义:声明(文字)形式和构造形式.即: var myObj = { key: value // ... }; 或: var myObj = new Object(); m ...

  5. JavaScript中的this—你不知道的JavaScript上卷读书笔记(三)

    this是什么? this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件.this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式.当一个函数被调用时,会 ...

  6. 《你不知道的javascript》读书笔记2

    概述 放假读完了<你不知道的javascript>上篇,学到了很多东西,记录下来,供以后开发时参考,相信对其他人也有用. 这篇笔记是这本书的下半部分,上半部分请见<你不知道的java ...

  7. 《你不知道的javascript》读书笔记1

    概述 放假读完了<你不知道的javascript>上篇,学到了很多东西,记录下来,供以后开发时参考,相信对其他人也有用. js的工作原理 引擎:从头到尾负责整个js的编译和运行.(很大一部 ...

  8. 《你不知道的JavaScript》读书笔记(二)词法作用域

    JavaScript 采用的是 词法作用域 的工作模型. 定义 词法化:大部分标准语言编译器的第一个工作阶段叫词法化(单词化),这个过程会对源代码中的字符进行检查,如果是有状态的解析过程,还会赋予单词 ...

  9. 《你不知道的JavaScript》读书笔记(一)作用域

    名词 引擎:从头到尾负责整个 JavaScript 程序的 编译 及 执行 过程. 编译器:负责 语法分析 及 代码生成. 作用域:负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套 ...

随机推荐

  1. Ubuntu11.04中如何将pycharm添加到系统的“应用程序”菜单里 (pycharm已成功安装)

    默认排序 Stu.zhouyc   21 人赞同了该回答 tools---->create desktop entry...不是很方便吗? 发布于 2016-04-09 21添加评论 分享 收藏 ...

  2. visual studio 2017发布dotnet core到docker

    docker的好处不用多说,有不了解的可移步<docker入门>,作为一个.net方面的老鸟也想早点搭上docker末班车,减少布署中的各种坑.以下我是在Visual Studio 201 ...

  3. navigate是Router类的一个方法,主要用来跳转路由。

    navigate是Router类的一个方法,主要用来跳转路由. 1 2 3 4 5 6 7 8 9 interface NavigationExtras {  relativeTo : Activat ...

  4. mysql 常用功能

    一.备份 mysqldump [OPTIONS] database [tables] http://www.blogjava.net/Alpha/archive/2007/08/10/135694.h ...

  5. 点滴积累【C#】---C#实现下载word

    效果: 思路: 简单的有两种方式下载,一种是流下载,一种是WriteFile下载.以下是使用WriteFile下载. 代码: protected void LinkButton1_Click(obje ...

  6. atitit.薄伽梵歌overview  attilax 读后感

    atitit.薄伽梵歌overview  attilax 读后感 1. 唯一一本记录神而不是神的代言人或者先知言论的经典 2 2. 篇章规模,字数 3 3. 内容摘要 3 4. 主要内容 3 4.1. ...

  7. CSU 1335: 高桥和低桥 (二分查找,树状数组)

    Description 有个脑筋急转弯是这样的:有距离很近的一高一低两座桥,两次洪水之后高桥被淹了两次,低桥却只被淹了一次,为什么?答案是:因为低桥太低了,第一次洪水退去之后水位依然在低桥之上,所以不 ...

  8. IntelliJ IDEA代码编码区提示库源不匹配字节码解决办法

    在使用IntelliJ IDEA进行开发时,可能会在代码编辑区出现此提示:library source does not match the bytecode for class HelloWorld ...

  9. cp/scp命令详解

    cp:拷贝命令 用法: cp [参数] source dest cp [参数] source ... directory 说明:将一个档案拷贝至另一个档案,或数个档案拷贝到另一目录 参数: -a 尽可 ...

  10. Ream的入门使用

    一.介绍 Realm是一个不错的手机平台上的数据库,支持多种编程环境,如:Java.Object-C.React Native.Swift.Xamari等. Realm的官网:https://real ...