赞
踩
作为一个iOS开发者,先吐槽一下JavaScript,学习JavaScript真的是无语,随意的语法,各种奇葩的设计(毕竟是10天左右设计出来的,肯定会有考虑不周的地方),总之写习惯了OC,写起来JS总是会感觉很不严谨,经常会忘记语法(跑偏到OC语法),吐槽完毕,下面开始正题!
本文主要讲述的是JavaScript相当核心的一个知识点--函数 Function,以及谈到函数不可避免的要谈到的神奇又令人头疼的的this!两者相辅相成,水乳交融,运用好的话,会事半功倍,但是如果搞不清楚其中的真正的含义,往往令人头疼不已,也是bug产生的隐患。下面详细讲解:
- function abs(x) {
- if (x >= 0) {
- return x;
- } else {
- return -x;
- }
- }
上述abs()
函数的定义如下:
function
指出这是一个函数定义;abs
是函数的名称;(x)
括号内列出函数的参数,多个参数以,
分隔;{ ... }
之间的代码是函数体,可以包含若干语句,甚至可以没有任何语句。请注意,函数体内部的语句在执行时,一旦执行到return
时,函数就执行完毕,并将结果返回。因此,函数内部通过条件判断和循环可以实现非常复杂的逻辑。
如果没有return
语句,函数执行完毕后也会返回结果,只是结果为undefined
。
由于JavaScript的函数也是一个对象,上述定义的abs()
函数实际上是一个函数对象,而函数名abs
可以视为指向该函数的变量。
因此,第二种定义函数的方式如下:
- var abs = function (x) {
- if (x >= 0) {
- return x;
- } else {
- return -x;
- }
- };
在这种方式下,function (x) { ... }
是一个匿名函数,它没有函数名。但是,这个匿名函数赋值给了变量abs
,所以,通过变量abs
就可以调用该函数。
上述两种定义完全等价,注意第二种方式按照完整语法需要在函数体末尾加一个;
,表示赋值语句结束。
二:函数的调用
调用函数时,按顺序传入参数即可:
- abs(10); // 返回10
- abs(-9); // 返回9
由于JavaScript允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多也没有问题,虽然函数内部并不需要这些参数:
- abs(10, 'blablabla'); // 返回10
- abs(-9, 'haha', 'hehe', null); // 返回9
传入的参数比定义的少也没有问题:
abs(); // 返回NaN
此时abs(x)
函数的参数x
将收到undefined
,计算结果为NaN
。
要避免收到undefined
,可以对参数进行检查:
- function abs(x) {
- if (typeof x !== 'number') {
- throw 'Not a number';
- }
- if (x >= 0) {
- return x;
- } else {
- return -x;
- }
- }
JavaScript还有一个免费赠送的关键字arguments
,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。arguments
类似Array
但它不是一个Array
:
- function foo(x) {
- console.log('x = ' + x); // 10
- for (var i=0; i<arguments.length; i++) {
- console.log('arg ' + i + ' = ' + arguments[i]); // 10, 20, 30
- }
- }
- foo(10, 20, 30);
log: x = 10
利用arguments
,你可以获得调用者传入的所有参数。也就是说,即使函数不定义任何参数,还是可以拿到参数的值:
- function abs() {
- if (arguments.length === 0) {
- return 0;
- }
- var x = arguments[0];
- return x >= 0 ? x : -x;
- }
-
- abs(); // 0
- abs(10); // 10
- abs(-9); // 9
关于函数的基础知识就讲解这么多,记住核心的一点:JavaScript的函数也是一个对象!这一点非常重要,对于后续理解this的指向。
在一个对象中绑定函数,称为这个对象的方法。
在JavaScript中,对象的定义是这样的:
- var xiaoming = {
- name: '小明',
- birth: 1990
- };
但是,如果我们给
xiaoming
绑定一个函数,就可以做更多的事情。比如,写个
age()
方法,返回
xiaoming
的年龄:
- var xiaoming = {
- name: '小明',
- birth: 1990,
- age: function () {
- var y = new Date().getFullYear();
- return y - this.birth;
- }
- };
-
- xiaoming.age; // function xiaoming.age()
- xiaoming.age(); // 今年调用是25,明年调用就变成26了
绑定到对象上的函数称为方法,和普通函数也没啥区别,但是它在内部使用了一个this
关键字,这个东东是什么?
在一个方法内部,this
是一个特殊变量,它始终指向当前对象,也就是xiaoming
这个变量。所以,this.birth
可以拿到xiaoming
的birth
属性。
让我们拆开写:
- function getAge() {
- var y = new Date().getFullYear();
- return y - this.birth;
- }
-
- var xiaoming = {
- name: '小明',
- birth: 1990,
- age: getAge
- };
-
- xiaoming.age(); // 25, 正常结果
- getAge(); // NaN
单独调用函数getAge()
怎么返回了NaN
?请注意,我们已经进入到了JavaScript的一个大坑里。
JavaScript的函数内部如果调用了this
,那么这个this
到底指向谁?
答案是,视情况而定!
如果以对象的方法形式调用,比如xiaoming.age()
,该函数的this
指向被调用的对象,也就是xiaoming
,这是符合我们预期的。
如果单独调用函数,比如getAge()
,此时,该函数的this
指向全局对象,也就是window
。
坑爹啊!
更坑爹的是,如果这么写:
- var fn = xiaoming.age; // 先拿到xiaoming的age函数
- fn(); // NaN
也是不行的!要保证this
指向正确,必须用obj.xxx()
的形式调用!
由于这是一个巨大的设计错误,要想纠正可没那么简单。ECMA决定,在strict模式下让函数的this
指向undefined
,因此,在strict模式下,你会得到一个错误:
- 'use strict';
-
- var xiaoming = {
- name: '小明',
- birth: 1990,
- age: function () {
- var y = new Date().getFullYear();
- return y - this.birth;
- }
- };
-
- var fn = xiaoming.age;
- fn(); // Uncaught TypeError: Cannot read property 'birth' of undefined
这个决定只是让错误及时暴露出来,并没有解决this
应该指向的正确位置。
有些时候,喜欢重构的你把方法重构了一下:
- 'use strict';
-
- var xiaoming = {
- name: '小明',
- birth: 1990,
- age: function () {
- function getAgeFromBirth() {
- var y = new Date().getFullYear();
- return y - this.birth;
- }
- return getAgeFromBirth();
- }
- };
-
- xiaoming.age(); // Uncaught TypeError: Cannot read property 'birth' of undefined
结果又报错了!原因是this
指针只在age
方法的函数内指向xiaoming
,在函数内部定义的函数,this
又指向undefined
了!(在非strict模式下,它重新指向全局对象window
!)
修复的办法也不是没有,我们用一个that
变量首先捕获this
:
- 'use strict';
-
- var xiaoming = {
- name: '小明',
- birth: 1990,
- age: function () {
- var that = this; // 在方法内部一开始就捕获this
- function getAgeFromBirth() {
- var y = new Date().getFullYear();
- return y - that.birth; // 用that而不是this
- }
- return getAgeFromBirth();
- }
- };
-
- xiaoming.age(); // 25
用
var that = this;
,你就可以放心地在方法内部定义其他函数,而不是把所有语句都堆到一个方法中。
在 JavaScript 中,this
是指当前函数中正在执行的上下文环境,因为这门语言拥有四种不同的函数调用类型:
alert('Hello World!')
console.log('Hello World!')
new RegExp('\\d')
alert.call(undefined, 'Hello World')
在以上每一项调用中,它都拥有各自独立的上下文环境,就会造成 this
所指意义有所差别。此外,严格模式也会对执行环境造成影响。
在以上每一项调用中,它都拥有各自独立的上下文环境,就会造成 this
所指意义有所差别。此外,严格模式也会对执行环境造成影响。
理解 this
关键字的关键在于理解各种不同的函数调用以及它是如何影响上下文环境的。
下面重点解释不同情况下的函数调用会怎样影响 this
以及判断上下文环境时会产生的一些常见陷阱。
函数调用 代表了该函数接收以成对的引号包含,用逗号分隔的不同参数组成的表达式。举例:parseInt('18')
。这个表达式不能是属性访问如 myObject.myFunction
这样会造成方法调用。[1, 5].join(',')
同样也不是一个函数调用而是方法调用。
函数调用的一个简单例子:
- function hello(name) {
- return 'Hello' + name + '!';
- }
- // 函数调用
- var message = hello('World');
- console.log(message); // => 'Hello World!'
hello('World')
是一个函数调用:hello
表达式代表了一个函数对象,接受了用成对引号包含的 World
参数。
高级一点的例子,立即执行函数 IIFE (immediately-invoked function expression):
- var message = (function(name) {
- return 'Hello ' + name + '!';
- })('World');
- console.log(message) // => 'Hello World!'
this
this
is the global object in a function invocation
全局对象取决于当前执行环境,在浏览器中,全局对象即 window
。
在函数调用中,上下文执行环境是全局对象,可以在以下函数中验证上下文:
- function sum(a, b) {
- console.log(this === window); // => true
- this.myNumber = 20; // 在全局对象中添加 'myNumber' 属性
- return a + b;
- }
- // sum() 为函数调用
- // this 在 sum() 中是全局对象 (window)
- sum(15, 16); // => 31
- window.myNumber; // => 20
当 sum(15, 16)
被调用时,JavaScript 自动将 this
设置为全局对象,即 window
。
当 this
在任何函数作用域以外调用时(最外层作用域:全局执行上下文环境),也会涉及到全局对象。
- console.log(this === window); // => true
- this.myString = 'Hello World!';
- console.log(window.myString); // => 'Hello World!'
console.log(this === window); // => true
this
this
is
undefined in a function invocation in strict mode
'use strict'
置于函数体的顶部。这样就可以将上下文环境中的 this
转为 undefined
。这样执行上下文环境不再是全局对象,与非严格模式刚好相反。
- function multiply(a, b) {
- 'use strict'; // 开启严格模式
- console.log(this === undefined); // => true
- return a * b;
- }
- // 严格模式下的函数调用 multiply()
- // this 在 multiply() 中为 undefined
- multiply(2, 5); // => 10
当 multiply(2, 5)
执行时,这个函数中的 this
是 undefined
。
严格模式不仅在当前作用域起到作用,它还会影响内部作用域,即内部声明的一切内部函数的作用域。
- function execute() {
- 'use strict'; // 开启严格模式
- function concat(str1, str2) {
- // 内部函数也是严格模式
- console.log(this === undefined); // => true
- return str1 + str2;
- }
- // 在严格模式下调用 concat()
- // this 在 concat() 下是 undefined
- concat('Hello', ' World!'); // => "Hello World!"
- }
- execute();
use strict
被插入函数执行主体的顶部,使严格模式可以控制到整个作用域。因为 concat
在执行作用域内部声明,因此它继承了严格模式。此外,concat('Hello', ' World!')
的调用中,this
也会成为 undefined
。
一个简单的 JavaScript 文件可能同时包含严格模式和非严格模式,所以在同一种类型调用中,可能也会有不同的上下文行为差异。
- function nonStrictSum(a, b) {
- // 非严格模式
- console.log(this === window); // => true
- return a + b;
- }
- function strictSum(a, b) {
- 'use strict';
- // 严格模式
- console.log(this === undefined); // => true
- return a + b;
- }
- // nonStrictSum() 在非严格模式下被调用
- // this 在 nonStrictSum() 中是 window 对象
- nonStrictSum(5, 6); // => 11
- // strictSum() 在严格模式下被调用
- // this 在 strictSum() 中是 undefined
- strictSum(8, 12); // => 20
this
在内部函数中一个常见的陷阱是理所应当的认为函数调用中的,内部函数中 this
等同于它的外部函数中的 this
。
正确的理解是内部函数的上下文环境取决于调用环境,而不是外部函数的上下文环境。
为了获取到所期望的 this
,应该利用间接调用修改内部函数的上下文环境,如使用 .call()
或者 .apply
或者创建一个绑定函数 .bind()
。
下面的例子表示计算两个数之和:
- var numbers = {
- numberA: 5,
- numberB: 10,
- sum: function() {
- console.log(this === numbers); // => true
- function calculate() {
- // 严格模式下, this 是 window or undefined
- console.log(this === numbers); // => false
- return this.numberA + this.numberB;
- }
- return calculate();
- }
- };
- numbers.sum(); // => 严格模式下,结果为 NaN 或者 throws TypeError
numbers.sum()
是对象内的一个方法调用,因此 sum
的上下文是 numbers
对象,而 calculate
函数定义在 sum
函数内,所以会误以为在 calculate
内 this
也指向的是 numbers
。
然而 calculate()
在函数调用(而不是作为方法调用)时,此时的 this
指向的是全局对象 window
或者在严格模式下指向 undefined
,即使外部函数 sum
拥有 numbers
对象作上下文环境,它也没有办法影响到内部的 this
。
numbers.sum()
调用的结果是 NaN
或者在严格模式下直接抛出错误 TypeError: Cannot read property 'numberA' of undefined
,而绝非期待的结果 5 + 10 = 15
,造成这样的原因是 calculate
并没有正确的被调用。
为了解决这个问题,正确的方法是使 calculate
函数被调用时的上下文同 sum
调用时一样,为了得到属性 numberA
和 numberB
,其中一种办法是使用 .call()
方法。
- var numbers = {
- numberA: 5,
- numberB: 10,
- sum: function() {
- console.log(this === numbers); // => true
- function calculate() {
- console.log(this === numbers); // => true
- return this.numberA + this.numberB;
- }
- // 使用 .call() 方法修改上下文环境
- return calculate.call(this);
- }
- };
- numbers.sum(); // => 15
calculate.call(this)
同样执行 calculate
函数,但是格外的添加了 this
作为第一个参数,修改了上下文执行环境。此时的 this.numberA + this.numberB
等同于 numbers.numberA + numbers.numberB
,其最终的结果就会如期盼的一样为 result 5 + 10 = 15
。
- var myObject = {
- // helloFunction 是对象中的方法
- helloFunction: function() {
- return 'Hello World!';
- }
- };
- var message = myObject.helloFunction();
helloFunction
是属于 myObject
的一个方法,调用这个方法可以使用属性访问的方式 myObject.helloFunction
。
方法调用表现为对象属性访问的形式,支持传入用成对引号包裹起来的一系列参数。上个例子中,myObject.helloFunction()
其实就是对象 myObject
上对属性 helloFunction
的方法调用。同样,[1, 2].join(',')
和 /\s/.test('beautiful world')
都是方法调用。
区分函数调用和方法调用是非常重要的,它们是不同类型的调用方式。主要的差别在于方法调用为访问属性的形式,如:.functionProperty()
或者 ['functionProperty']()
,而函数调用为 ()
。
- ['Hello', 'World'].join(', '); // 方法调用
- ({ ten: function() { return 10; } }).ten(); // 方法调用
- var obj = {};
- obj.myFunction = function() {
- return new Date().toString();
- };
- obj.myFunction(); // 方法调用
-
- var otherFunction = obj.myFunction;
- otherFunction(); // 函数调用
- parseFloat('16.60'); // 函数调用
- isNaN(0); // 函数调用
this
this
is the
object that owns the method in a method invocation
this
代表的是对象它自身。让我们创建一个对象,其包含一个可以递增属性的方法。- var calc = {
- num: 0,
- increment: function() {
- console.log(this === calc); // => true
- this.num += 1;
- return this.num;
- }
- };
- // 方法调用,this 指向 calc
- calc.increment(); // => 1
- calc.increment(); // => 2
calc.increment()
调用意味着上下文执行环境在 calc
对象里,因此使用 this.sum
递增 num
这个属性是可行的。
一个 JavaScript 对象继承方法来自于它自身的属性。当一个被继承方法在对象中调用时,上下文执行环境同样是对象本身。
- var myDog = Object.create({
- sayName: function() {
- console.log(this === myDog); // => true
- return this.name;
- }
- });
- myDog.name = 'Milo';
- // 方法调用, this 指向 myDog
- myDog.sayName(); // => 'Milo'
Object.create()
创建了一个新的对象 myDog
并且设置了属性,myDog
对象继承了 myName
方法。当 myDog.sayName()
被执行时,上下文执行环境指向 myDog
。
在 ECMAScript 5 的 class
语法中, 方法调用指的是实例本身。
- class Planet {
- constructor(name) {
- this.name = name;
- }
- getName() {
- console.log(this === earth); // => true
- return this.name;
- }
- }
- var earth = new Planet('Earth');
- // 方法调用,上下文为 earth
- earth.getName(); // => 'Earth'
一个对象中的方法可能会被提取抽离成一个变量。当使用这个变量调用方法时,开发者可能会误认为 this
指向的还是定义该方法时的对象。
如果方法调用不依靠对象,那么就是一个函数调用,即 this
指向全局对象 object
或者在严格模式下为 undefined
。创建函数绑定可以修复上下文,使该方法被正确对象调用。
下面的例子创建了构造器函数 Animal
并且创建了一个实例 myCat
,在 setTimeout()
定时器 1s 后打印 myCat
对象信息。
- function Animal(type, legs) {
- this.type = type;
- this.legs = legs;
- this.logInfo = function() {
- console.log(this === myCat); // => false
- console.log('The ' + this.type + ' has ' + this.legs + ' legs');
- }
- }
- var myCat = new Animal('Cat', 4);
- // 打印出 "The undefined has undefined legs"
- // 或者在严格模式下抛出错误 TypeError
- setTimeout(myCat.logInfo, 1000);
开发者可能认为在 setTimeout
下调用 myCat.logInfo()
会打印出 myCat
对象的信息。但实际上这个方法被分离了出来作为了参数传入函数内 setTimeout(myCat.logInfo)
,然后 1s 后会发生函数调用。当 logInfo
被作为函数调用时,this
指向全局对象 window
或者在严格模式下为 undefined
,因此对象信息没有正确地被打印。
方法绑定可以使用 .bind()
方法。如果被分离的方法绑定了 myCat
对象,那么上下文问题就可以被解决了:
- function Animal(type, legs) {
- this.type = type;
- this.legs = legs;
- this.logInfo = function() {
- console.log(this === myCat); // => true
- console.log('The ' + this.type + ' has ' + this.legs + ' legs');
- };
- }
- var myCat = new Animal('Cat', 4);
- // 打印 "The Cat has 4 legs"
- setTimeout(myCat.logInfo.bind(myCat), 1000);
此时,myCat.logInfo.bind(myCat)
返回的新函数调用里的 this
指向了 myCat
。
构造函数调用使用 new
关键词,后面跟随可带参数的对象表达式,例:new RegExp('\\d')
。
以下的例子声明了一个构造函数 Country
,并调用。
- function Country(name, traveled) {
- this.name = name ? this.name : 'United Kingdom';
- this.traveled = Boolean(traveled); // 转换为 boolean 值
- }
- Country.prototype.travel = function() {
- this.traveled = true;
- };
- // 构造函数调用
- var france = new Country('France', false);
- // 构造函数调用
- var unitedKingdom = new Country;
-
- france.travel(); // Travel to France
new City('Paris')
是一个构造器调用,这个对象初始化使用了类中特殊的方法 constructor
,其中的 this
指向的是新创建的对象。
构造器调用创建了一个空的新对象,从构造器的原型中继承属性。这个构造器函数的意义在于初始化对象,因此这个类型的函数调用创建实例。
当一个属性访问 myObject.myFunction
前拥有 new
关键词,那么 JavaScript 会执行构造器调用而不是方法调用。举个例子:new myObject.myFunction()
意味着首先这个函数会解析为一个属性访问函数 extractedFunction = myObject.myFunction
,然后用构造器创建一个新对象 new extractedFunction
。
this
this
is the
newly created object in a constructor invocation
构造器调用的环境是新创建的对象。通过传递构造函数参数来初始化新建的对象,添加属性初始化值以及事件处理器。
让我们来验证以下这个例子的上下文环境:
- function Foo () {
- console.log(this instanceof Foo); // => true
- this.property = 'Default Value';
- }
- // 构造函数调用
- var fooInstance = new Foo();
- fooInstance.property; // => 'Default Value'
new Foo()
建立构造器调用,它的上下文环境为 fooInstance
,在 Foo
对象中初始化了 this.property
这个属性并赋予初始值。
在使用 class
语法时也是同样的情况(在 ES6 中),初始化只发生在它的 constructor
方法中。
- class Bar {
- constructor() {
- console.log(this instanceof Bar); // => true
- this.property = 'Default Value';
- }
- }
- // 构造函数调用
- var barInstance = new Bar();
- barInstance.property; // => 'Default Value'
当执行 new Bar()
时,JavaScript 创建了一个空对象并且它的上下文环境为 constructor
方法,因此添加属性的办法是使用 this
关键词:this.property = 'Default Value'
。
new
关键词RegExp
的例子:
- var reg1 = new RegExp('\\w+');
- var reg2 = RegExp('\\w+');
-
- reg1 instanceof RegExp; // => true
- reg2 instanceof RegExp; // => true
- reg1.source === reg2.source; // => true
当执行 new RegExp('\\w+')
和 RegExp('\\w+')
时,JavaScript 创建了两个相等的普通表达式对象。
但是使用函数调用创建对象会产生潜在的问题(包括工厂模式),当失去了 new
关键词,一些构造器会取消初始化对象。
以下例子描述了这个问题:
- function Vehicle(type, wheelsCount) {
- this.type = type;
- this.wheelsCount = wheelsCount;
- return this;
- }
- // 函数调用
- var car = Vehicle('Car', 4);
- car.type; // => 'Car'
- car.wheelsCount // => 4
- car === window // => true
Vehicle
是一个在对象上设置了 type
和 wheelsCount
属性的函数。
当执行了 Vehicle('Car', 4)
时,会返回对象 car
,它拥有正确的属性值:car.type
指向 Car
,car.wheelsCount
指向 4
,开发者会误以为这样创建初始化对象没有什么问题。
然而,当前执行的是函数调用,因此 this
指向的是 window
对象,所以它设置的属性其实是挂在 window
对象上的,这样是完全错误的,它并没有创建一个新对象。
应该正确的执行方式是使用 new
关键词来保证构造器被正确调用:
- function Vehicle(type, wheelsCount) {
- if (!(this instanceof Vehicle)) {
- throw Error('Error: Incorrect invocation');
- }
- this.type = type;
- this.wheelsCount = wheelsCount;
- return this;
- }
- // 构造函数调用
- var car = new Vehicle('Car', 4);
- car.type // => 'Car'
- car.wheelsCount // => 4
- car instanceof Vehicle // => true
-
- // 函数调用,会报错。
- var brokenCat = Vehicle('Broken Car', 3);
new Vehicle('Car', 4)
可以正确运行:一个新的对象被创建和初始化,因为 new
关键词代表了当前为构造器调用。this instanceof Vehicle
,可以保证当前的执行上下文是正确的对象类型。如果 this
不是指向 Vehicle
,那么就存在错误。 如果 Vehicle('Broken Car', 3)
表达式没有 new
关键词而被执行,就会抛出错误:Error: Incorrect invocation
。
间接调用表现为当一个函数使用了 .call()
或者 .apply()
方法。
在 JavaScript 中,函数为一等对象,这意味着函数是一个对象,对象类型即为 Function
。
在函数的一系列方法中,.call()
和 .apply()
被用来配置当前调用的上下文环境。
方法 .call(thisArg[, arg1[, arg2[, ...]]])
接收第一个参数 thisArg
作为执行的上下文环境,以及一系列参数 arg1, arg2, ...
作为函数的传参被调用。
并且,方法 .apply(thisArg, [args])
接收 thisArg
作为上下文环境,剩下的参数可以用类数组对象 [args]
传递。
间接调用的例子:
- function increment(number) {
- return ++number;
- }
- increment.call(undefined, 10); // => 11
- increment.apply(undefined, [10]); // => 11
increment.call()
和 increment.apply()
同时传递了参数 10
调用 increment
函数。
两个方法最主要的区别为 .call()
接收一组参数,如 myFunction.call(thisValue, 'value1', 'value2')
,而 .apply()
接收一串参数作为类数组对象传递,如 myFunction.apply(thisValue, ['value1', 'value2'])
。
this
this
is the
first argument of
.call()
or
.apply()
in an indirect invocation
this
指向的是 .call()
和 .apply()
传递的第一个参数。- var rabbit = { name: 'White Rabbit' };
- function concatName(string) {
- console.log(this === rabbit); // => true
- return string + this.name;
- }
- // 间接调用
- concatName.call(rabbit, 'Hello '); // => 'Hello White Rabbit'
- concatName.apply(rabbit, ['Bye ']); // => 'Bye White Rabbit'
当函数执行需要特别指定上下文时,间接调用非常有用,它可以解决函数调用中的上下文问题(this
指向 window
或者严格模式下指向 undefined
),同时也可以用来模拟方法调用对象。
另一个实践例子为,在 ES5 中的类继承中,调用父级构造器。
- function Runner(name) {
- console.log(this instanceof Rabbit); // => true
- this.name = name;
- }
- function Rabbit(name, countLegs) {
- console.log(this instanceof Rabbit); // => true
- // 间接调用,调用了父级构造器
- Runner.call(this, name);
- this.countLegs = countLegs;
- }
- var myRabbit = new Rabbit('White Rabbit', 4);
- myRabbit; // { name: 'White Rabbit', countLegs: 4 }
Runner.call(this, name)
在 Rabbit
里间接调用了父级方法初始化对象。
因为函数调用会极大地影响到 this
,所以从现在开始不要直接问自己:
this
是从哪里来的?
而是要开始思考:
当前函数是怎么被调用的?
以上思路可以帮助开发者减少判断 this
带来的烦恼。
如有错误,欢迎指正!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。