第一章  关于this

1、this 既不指向函数自身也不指向函数的词法作用域

2、this 实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用(调用位置)。

第二章 this全面解析

调用栈与调用位置

function baz() {
// 当前调用栈是:baz
// 因此,当前调用位置是全局作用域
  console.log( "baz" );
  bar(); // <-- bar 的调用位置
}
function bar() {
// 当前调用栈是 baz -> bar
// 因此,当前调用位置在 baz 中
  console.log( "bar" );
  foo(); // <-- foo 的调用位置
}
function foo() {
// 当前调用栈是 baz -> bar -> foo
// 因此,当前调用位置在 bar 中
  console.log( "foo" );
}
baz(); // <-- baz 的调用位置

绑定规则【四个】:

1、默认绑定 

最常用的函数调用类型:独立函数调用。但是在严格模式下是不可行的。

this 的绑定规则完全取决于调用位置,但是只有 foo() 运行在非 strict mode 下时,默认绑定才能绑定到全局对象;严格模式下与 foo()的调用位置无关

例子

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

如果使用严格模式(strict mode),那么全局对象将无法使用默认绑定,因此 this 会绑定 到 undefined:

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

虽然 this 的绑定规则完全取决于调用位置,但是只 有 foo() 运行在非 strict mode 下时,默认绑定才能绑定到全局对象;严格模式下与 foo() 的调用位置无关【但是通常不会混用】

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

2、隐式绑定

例子:因为当 foo() 被调用时,它的落脚点确实指向 obj 对象。当函数引 用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。因为调 用 foo() 时 this 被绑定到 obj,因此 this.a 和 obj.a 是一样的

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

最近原则 对象属性引用链中只有最顶层或者说最后一层会影响调用位置

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

3、显示绑定(不想在对象内部包含函数引用。而是在某个对象上强制调用函数)

解决方法:使用函数的call()和apply()方法,这两个方法的第一个参数是一个对象,他们会把这个对象绑定到this,接着调用函数时指定这个this。

(1)call()方法  通过 foo.call(..),我们可以在调用 foo 时强制把它的 this 绑定到 obj 上。

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

硬绑定 可以解决丢失绑定问题

(2)硬绑定的典型应用场景就是创建一个包裹函数,传入所有的参数并返回收到的所有值

例如

 function foo(something) {
console.log( this.a, something );
return this.a + something;
} var obj = {
a:2
}; var bar = function() {
return foo.apply( obj, arguments );
}; var b = bar( 3 ); // 2 3
console.log( b ); //

(3)另外一个使用方法是创建一个i可以重复使用的辅助函数

function foo(something) {
console.log( this.a, something );
return this.a + something;
} // 简单的辅助绑定函数
function bind(fn, obj) {
return function() {
return fn.apply( obj, arguments );
};
} var obj = {
a:2
}; var bar = bind( foo, obj ); var b = bar( 3 ); // 2 3
console.log( b ); //

噔噔噔噔.....由于硬绑定是一个非常常用的模式,所以在ES5中提供了内置的方法 Function.prototype. bind,使用方法如下:

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

 function foo(something) {
console.log( this.a, something );
return this.a + something;
}
var obj = {
a:2
}; var bar = foo.bind( obj );
var b = bar( 3 ); // 2 3
console.log( b ); //

4、new绑定

JavaScript的“构造函数”只是一些 使用 new 操作符时被调用的函数。它们并不会属于某个类,也不会实例化一个类。实际上, 它们甚至都不能说是一种特殊的函数类型,它们只是被 new 操作符调用的普通函数而已

第一章  关于this

1、this 既不指向函数自身也不指向函数的词法作用域

2、this 实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用(调用位置)。

第二章 this全面解析

调用栈与调用位置

function baz() {
// 当前调用栈是:baz
// 因此,当前调用位置是全局作用域
  console.log( "baz" );
  bar(); // <-- bar 的调用位置
}
function bar() {
// 当前调用栈是 baz -> bar
// 因此,当前调用位置在 baz 中
  console.log( "bar" );
  foo(); // <-- foo 的调用位置
}
function foo() {
// 当前调用栈是 baz -> bar -> foo
// 因此,当前调用位置在 bar 中
  console.log( "foo" );
}
baz(); // <-- baz 的调用位置

绑定规则【四个 默认绑定 隐式绑定 显式绑定 new绑定】:

1、默认绑定 

最常用的函数调用类型:独立函数调用。但是在严格模式下是不可行的。

this 的绑定规则完全取决于调用位置,但是只有 foo() 运行在非 strict mode 下时,默认绑定才能绑定到全局对象;严格模式下与 foo()的调用位置无关

例子

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

如果使用严格模式(strict mode),那么全局对象将无法使用默认绑定,因此 this 会绑定 到 undefined:

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

虽然 this 的绑定规则完全取决于调用位置,但是只 有 foo() 运行在非 strict mode 下时,默认绑定才能绑定到全局对象;严格模式下与 foo() 的调用位置无关【但是通常不会混用】

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

2、隐式绑定

例子:因为当 foo() 被调用时,它的落脚点确实指向 obj 对象。当函数引 用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。因为调 用 foo() 时 this 被绑定到 obj,因此 this.a 和 obj.a 是一样的

 1 function foo() {
2   console.log( this.a );
3 }
4
5 var obj = {
6   a: 2,
7   foo: foo
8 };
9
10 obj.foo(); // 2

最近原则 对象属性引用链中只有最顶层或者说最后一层会影响调用位置

 1 function foo() {
2   console.log( this.a );
3 }
4
5 var obj2 = {
6   a: 42,
7   foo: foo
8 };
9
10 var obj1 = {
11   a: 2,
12   obj2: obj2
13 };
14
15 obj1.obj2.foo(); // 42

3、显式绑定(不想在对象内部包含函数引用。而是在某个对象上强制调用函数)

解决方法:使用函数的call()和apply()方法,这两个方法的第一个参数是一个对象,他们会把这个对象绑定到this,接着调用函数时指定这个this。

(1)call()方法  通过 foo.call(..),我们可以在调用 foo 时强制把它的 this 绑定到 obj 上。

1 function foo() {
2 console.log( this.a );
3 }
4
5 var obj = {
6 a:2
7 };
8
9 foo.call( obj ); // 2

硬绑定 可以解决丢失绑定问题

(2)硬绑定的典型应用场景就是创建一个包裹函数,传入所有的参数并返回收到的所有值

例如

 1 function foo(something) {
2 console.log( this.a, something );
3 return this.a + something;
4 }
5
6 var obj = {
7 a:2
8 };
9
10 var bar = function() {
11 return foo.apply( obj, arguments );
12 };
13
14 var b = bar( 3 ); // 2 3
15 console.log( b ); // 5

(3)另外一个使用方法是创建一个i可以重复使用的辅助函数

function foo(something) {
console.log( this.a, something );
return this.a + something;
} // 简单的辅助绑定函数
function bind(fn, obj) {
return function() {
return fn.apply( obj, arguments );
};
} var obj = {
a:2
}; var bar = bind( foo, obj ); var b = bar( 3 ); // 2 3
console.log( b ); // 5

噔噔噔噔.....由于硬绑定是一个非常常用的模式,所以在ES5中提供了内置的方法 Function.prototype. bind,使用方法如下:

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

 1 function foo(something) {
2 console.log( this.a, something );
3 return this.a + something;
4 }
5 var obj = {
6 a:2
7 };
8
9 var bar = foo.bind( obj );
10 var b = bar( 3 ); // 2 3
11 console.log( b ); // 5

4、new绑定

JavaScript的“构造函数”只是一些 使用 new 操作符时被调用的函数。它们并不会属于某个类,也不会实例化一个类。实际上, 它们甚至都不能说是一种特殊的函数类型,它们只是被 new 操作符调用的普通函数而已。

包括内置对象函数(比如 Number(..) ,详情请查看第 3 章)在内的所有函数都可
以用 new 来调用,这种函数调用被称为构造函数调用。这里有一个重要但是非常细微的区
别:实际上并不存在所谓的“构造函数”,只有对于函数的“构造调用”。

使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

1. 创建(或者说构造)一个全新的对象。
2. 这个新对象会被执行 [[ 原型 ]] 连接。
3. 这个新对象会绑定到函数调用的 this 。
4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。

例如

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

使用 new 来调用 foo(..) 时,我们会构造一个新对象并把它绑定到 foo(..) 调用中的 this上。 new 是最后一种可以影响函数调用时 this 绑定行为的方法,我们称之为 new 绑定。

5、优先级

这四个绑定的优先级   new绑定 > 显式绑定 > 隐式绑定 > 默认绑定

判断 this
现在我们可以根据优先级来判断函数在某个调用位置应用的是哪条规则。可以按照下面的
顺序来进行判断:

1. 由 new 调用?绑定到新创建的对象。
2. 由 call 或者 apply (或者 bind )调用?绑定到指定的对象。
3. 由上下文对象调用?绑定到那个上下文对象。
4. 默认:在严格模式下绑定到 undefined ,否则绑定到全局对象。

6、绑定例外

更安全的this

在 JavaScript 中创建一个空对象最简单的方法都是 Object.create(null)(详细介绍请看第 5 章)。 Object.create(null) 和 {} 很像,但是并不会创建 Object.prototype 这个委托,所以它比 {} “更空”:

 1 function foo(a,b) {
2 console.log( "a:" + a + ", b:" + b );
3 }
4 // 我们的 DMZ 空对象
5 var ø = Object.create( null );
6 // 把数组展开成参数
7 foo.apply( ø, [2, 3] ); // a:2, b:3
8 // 使用 bind(..) 进行柯里化
9 var bar = foo.bind( ø, 2 );
10 bar( 3 ); // a:2, b:3

间接引用 间接引用最容易在赋值时发生:

1 function foo() {
2 console.log( this.a );
3 }
4 var a = 2;
5 var o = { a: 3, foo: foo };
6 var p = { a: 4 };
7 o.foo(); // 3
8 (p.foo = o.foo)(); // 2

赋值表达式 p.foo = o.foo 的返回值是目标函数的引用,因此调用位置是 foo() 而不是p.foo() 或者 o.foo() 。根据我们之前说过的,这里会应用默认绑定。

注意:对于默认绑定来说,决定 this 绑定对象的并不是调用位置是否处于严格模式,而是函数体是否处于严格模式。如果函数体处于严格模式, this 会被绑定到 undefined ,否则this 会被绑定到全局对象。

软绑定

给默认绑定指定一个全局对象和 undefined 以外的值,那就可以实现和硬绑定相同的效果,同时保留隐式绑定或者显式绑定修改 this 的能力。 softBind(..)

7、this词法

ES6 中的箭头函数并不会使用四条标准的绑定规则,而是根据当前的词法作用域来决定this ,具体来说,箭头函数会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。这其实和 ES6 之前代码中的 self = this 机制一样。

例如:foo() 内部创建的箭头函数会捕获调用时 foo() 的 this 。由于 foo() 的 this 绑定到 obj1 ,bar (引用箭头函数)的 this 也会绑定到 obj1 ,箭头函数的绑定无法被修改。( new 也不行!)

 1 function foo() {
2 // 返回一个箭头函数
3 return (a) => {
4 //this 继承自 foo()
5 console.log( this.a );
6 };
7 }
8 var obj1 = {
9 a:2
10 };
11 var obj2 = {
12 a:3
13 };
14 var bar = foo.call( obj1 );
15 bar.call( obj2 ); // 2, 不是 3

其实,箭头函数最常用于回调函数中,例如事件处理器或者定时器

function foo() {
setTimeout(() => {
// 这里的 this 在此法上继承自 foo()
console.log( this.a );
},100);
}
var obj = {
a:2
};
foo.call( obj ); // 2

而且箭头函数可以像bin()一样确保函数的this被绑定到指定的对象,其重要性还体现在它用更常见的词法作用域取代了传统的 this 机制.

你不知道的JavaScript(上)this和对象原型(一)的更多相关文章

  1. 读书笔记-你不知道的JavaScript(上)

    本文首发在我的个人博客:http://muyunyun.cn/ <你不知道的JavaScript>系列丛书给出了很多颠覆以往对JavaScript认知的点, 读完上卷,受益匪浅,于是对其精 ...

  2. 《你不知道的javascript(上)》笔记

    作用域是什么 编译原理 分词/词法分析 这个过程会将由字符组成的字符串分解成(对编程语言来说)有意义的代码块,这些代码块被称为词法单元 解析/语法分析 词法单元流(数组)转换成一个由元素逐级嵌套所组成 ...

  3. 你不知道的JS之 this 和对象原型(一)this 是什么

     原文:你不知道的js系列 JavaScript 的 this 机制并没有那么复杂 为什么会有 this? 在如何使用 this 之前,我们要搞清楚一个问题,为什么要使用 this. 下面的代码尝试去 ...

  4. 《你不知道的Javascript》感悟篇—对象属性遍历的那些事

    划重点 本篇笔者将重点介绍JavaScript中 getOwnPropertyNames .Object.keys.for ... in 的使用及他们之间的异同点. getOwnPropertyNam ...

  5. JavaScript 之 原型对象、对象原型 —— { }

    JavaScript -- 构造函数 // 构造函数 function Player(name, age) { this.name = name; this.age = age; } JavaScri ...

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

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

  7. 《你必须知道的javascript(上)》- 2.this与对象原型

    1 关于this 1.1 为什么使用this 随着你的使用模式越来越复杂,显式传递上下文对象会让代码变得越来越混乱,使用this则不会这样.当我们介绍对象和原型时,你就会明白函数可以自动引用合适的上下 ...

  8. 《你不知道的JavaScript》整理(三)——对象

    一.语法 两种形式定义:文字形式和构造形式. //文字形式 var myObj = { key: value }; //构造形式 var myObj = new Object(); myObj.key ...

  9. 关于Javascript中通过实例对象修改原型对象属性值的问题

    Javascript中的数据值有两大类:基本类型的数据值和引用类型的数据值. 基本类型的数据值有5种:null.undefined.number.boolean和string. 引用类型的数据值往大的 ...

  10. Javascript函数、构造函数、原型、类和对象

    函数 函数是JavaScript中特殊的对象,对函数执行typeof运算会返回字符串"function",因为函数也是对象,他们可以拥有属性和方法. 静态方法 函数在JS中定义了类 ...

随机推荐

  1. python2中的SSL:CERTIFICATE_VERIFY_FAILED错误的解决办法

    在使用urllib2访问一个自签名的https链接时,对于python2.6以下版本,TLS握手期间是不会检查服务器X509的证书签名是否是CA的可信任根证书.不过python2.7以后改变了这种情况 ...

  2. [ML机器学习 - Stanford University] - Week1 - 01 Introduction

    What is Machine Learning? Two definitions of Machine Learning are offered. Arthur Samuel described i ...

  3. [ch02-03] 梯度下降

    系列博客,原文在笔者所维护的github上:https://aka.ms/beginnerAI, 点击star加星不要吝啬,星越多笔者越努力. 2.3 梯度下降 2.3.1 从自然现象中理解梯度下降 ...

  4. PHP中echo与print语句的实例教程

    在 PHP 中,有两种基本的输出方法:echo 和 print. echo与print的差异 echo能够输出一个以上的字符串. print只能输出一个字符串,并始终返回 1. 提示:echo 比 p ...

  5. STDN: Scale-Transferrable Object Detection论文总结

    概述 STDN是收录于CVPR 2018的一篇目标检测论文,提出STDN网络用于提升多尺度目标的检测效果.要点包括:(1)使用DenseNet-169作为基础网络提取特征:(2)提出Scale-tra ...

  6. Java的 FileWriter类 和 FileReader类

    一.FileReader类1,构造方法:FileReader fr = new FileReader(String fileName);//使用带有指定文件的String参数的构造方法.创建该输入流对 ...

  7. 两个对象值相同(x.equals(y)==true),但却可有不同的hashcode这句话对吗?

    1.这句话当然不对啦,请参看官方文档给出的解释! hashCode public int hashCode()返回该对象的哈希码值.支持此方法是为了提高哈希表(例如 java.util.Hashtab ...

  8. if判断语句的总结

    1.表达式:关系表达式或逻辑表达式: 2.表达式的运算结果应该是“真”或者“假”: 真:执行该语句:            假:跳过该语句,执行下一条语句: 3.“语句”可以是单语句也可以是复合语句: ...

  9. 2019-2020-3 20199317《Linux内核原理与分析》第三周作业

    第2章  操作系统是如何工作的 1  计算机的三大法宝      存储程序计算机:冯诺依曼结构 函数调用堆栈机制:记录调用的路径和参数的空间 中断机制:由CPU和内核代码共同实现了保存现场和恢复现场, ...

  10. 挑战10个最难的Java面试题(附答案)【下】

    查看挑战10个最难的Java面试题(附答案)[上] 在本文中,我们将从初学者和高级别进行提问, 这对新手和具有多年 Java 开发经验的高级开发人员同样有益. 关于Java序列化的10个面试问题 大多 ...