11. 函数:内部特殊值和函数方法
- 罗列 this 值的常见情况。
另一个特殊的对象是 this。this 的值取决于多种因素。
(1) 全局上下文
this 在全局上下文中,即在任何函数体的外部时,都指向全局对象,在浏览器宿主环境中就是 window。
来看下面的例子:
let o1 = {
this: this,
o2: {
this: [this],
},
};
console.log(o1.this === window); // true
console.log(o1.o2.this[0] === window); // true
在这个例子中,对象 o1
的 this 属性引用 this 值,而这个 this 值在全局上下文中指向 window。同样的,o2 的 this 值引用一个数组对象,这个数组的第一个元素为 this,但无论如何 this 值都在全局上下文中,所以都指向 window。
(2) 标准函数上下文
this 出现在标准函数上下文中,此时的 this 取决于函数调用的方式。
- 在非严格模式下直接调用标准函数,则其内部的 this 指向全局对象。
来看下面几个例子:
function getThis() {
return this;
}
console.log(getThis() === window);
// -> true
在这个例子中,getThis 是一个函数声明,它返回内部的 this 值。之后在全局上下文中直接调用了 getThis,它返回了全局对象。
非严格模式下直接调用标准函数,其内部的 this 值指向全局对象,而不论调用的上下文:
function foo() {
function getThis() {
console.log(this === window);
// -> true
}
getThis();
}
foo();
在这个例子中,getThis 在函数 foo 的上下文中直接调用,但不论是在全局上下文还是函数上下文中,非严格模式下直接调用标准函数,其内部的 this 都指向全局对象。
- 严格模式下直接调用标准函数,则其内部的 this 为 undefined。
来看下面几个例子:
function getThisInStrict() {
'use strict';
return this;
}
console.log(getThisInStrict() === window);
// -> false
console.log(getThisInStrict() === undefined);
// -> true
在这个例子中,函数 getThisInStrict 的作用域内使用了严格模式。之后,在全局上下文中调用了 getThisInStrict,它内部的 this 为 undefined 而不是全局对象。
同样的,严格模式下直接调用标准函数,其内部的 this 为 undefined,而不论调用的上下文。
- 标准函数作为方法调用时,this 指向直接调用者,而非间接调用者。
来看下面几个例子:
function getThis() {
return this;
}
let o = {
name: 'o',
};
o.getThis = getThis;
console.log(o.getThis().name);
// -> 'o'
在这个例子中,getThis 作为 o 的方法在全局上下文中调用。getThis 内部的 this 指向 o。
这个规则不受严格模式和调用方法时的上下文影响:
'use strict';
function getThis() {
return this;
}
function foo() {
const o = {
name: 'o',
};
o.getThis = getThis;
console.log(o.getThis().name);
// -> 'o'
}
foo();
在这个例子中,全局使用了严格模式。之后在函数 foo
上下文中,getThis
作为 o 的方法被调用。其内部的 this 指向 o。
复杂的情况是通过间接方式调用函数:
function getThis() {
return this;
}
console.log(getThis.prototype.constructor === getThis);
// -> true
console.log(getThis.prototype.constructor() === getThis.prototype);
// -> true
let constructor = getThis.prototype.constructor;
console.log(constructor() === window);
// -> true
在这个例子中,getThis.prototype.constructor
返回getThis
函数对象本身。在执行 getThis.prototype.constructor()
时,getThis
的直接调用者为getThis.prototype
,因此 this 指向getThis.prototype
。之后,将 getThis.prototype.constructor
赋给 constructor,并直接调用,因此 this 指向全局对象。
- 标准函数作为构造函数调用,this 指向正在构建的对象:
function Person() {
this.name = 'Nicholas';
console.log(this instanceof Person);
// -> true
}
new Person();
在这个例子中,Person 作为构造方法调用,this 指向被构建的对象,因此 this instanceOf Person
为 true。
(3) 箭头函数
箭头函数的 this 行为和标准函数的截然不同。
- 箭头函数在全局上下文中,则其 this 绑定全局对象,且严格模式和方法调用对该 this 没有影响。
来看下面几个例子:
const getThisInArrowFunc = () => {
'use strict';
return this;
};
console.log(getThisInArrowFunc() === window);
// -> true
在这个例子中,getThisInArrowFunc
位于全局上下文,其内部使用了严格模式,但这不影响 this 的值。this 依旧绑定全局对象。
var name = 'window';
const o1 = {
name: 'o1',
o2: {
name: 'o2',
getThis: () => this,
},
};
console.log(o1.o2.getThis().name);
// -> 'window'
在这个例子中,getThis
作为 o2
的方法被o1
间接在全局上下文中调用,但不论是否作为方法被调用,getThis
都在全局上下文中,因此其内部的 this 还是绑定全局对象。
- 箭头函数在函数上下文中,则其 this 绑定紧邻的外层函数的 this。
var name = 'window';
function foo() {
const o1 = {
name: 'o1',
o2: {
name: 'o2',
getThis: () => this,
},
};
console.log(o1.o2.getThis().name); // window
}
foo();
在这个例子中,箭头函数 getThis
在foo
函数的上下文中。这里 foo
在非严格模式下直接调用,所以函数 foo
的 this 指向全局对象,箭头函数绑定了该值。
当然,如过外层的函数foo
以方法调用,则该箭头函数内部的 this 就绑定 foo
的直接调用者。
new.target
ECMAScript
中的函数始终可以作为构造函数实例化一个新对象,也可以作为普通函数被调用。ECMAScript 6
新增了检测函数是否使用 new 关键字调用的 new.target
属性。如果函数是正常调用的,则new.target
的值是 undefined;如果是使用 new 关键字调用的,则 new.target
将引用被调用的构造函数。
来看这个例子:
function foo() {
const funcName = arguments.callee.name;
if (new.target) {
throw `函数${funcName}不能用作构造函数!`;
} else {
console.log(`函数${funcName}用为非构造函数!`);
}
}
new foo();
foo();
在这个例子中,函数foo
的名字使用 arguments.callee.name
取得,如果foo
用作构造函数,则new.target
指向构造函数本身,为非空对象,则抛出错误。如果foo
没有用作构造函数,则 new.target
为 undefined,正常执行。
arguments 对象
aruments
对象是一个类数组对象。
注意:
arugments[i]
对应的参数名如name1
内存是分开的,只不过是保持同步- 如果只传如了一个参数,想要通过
arugments[1]
来设置第二个参数是不可行的 - 严格模式下,修改
aruguments
无法影响到num2
的值 , 在函数中重写aruguments
会报错
- 模拟
Function.prototype.call
方法- 不传入第一个参数,默认为
window
- 给新的对象添加一个函数,然后执行完再删除 ```javascript //call const { log } = console;
- 不传入第一个参数,默认为
Function.prototype.myCall = function (thisArg, …args) { thisArg ? thisArg : whindow thisArg.fn = this let res = thisArg.fn(…args); delete thisArg.fn return res; };
function sum(num1,num2) { return num1 + num2; } console.log(sum.myCall(this,3,2)); // 5
5. 学习 apply,bind 方法
- `bind() :` 会创建一个新的函数实例,其`this`值会绑定传给`bind()`的对象.
```javascript
color = 'red';
let o = {
color : 'blue'
}
function sayColor() {
console.log(this.color);
}
sayColor.bind(o)(); //blue
apply() :
接受两个参数: 函数内this
的值和一个参数数组与call()
不同的是call
必须一个一个传- 模拟 bind 方法
const { log } = console;
Function.prototype.myBind = function (thisArg, ...args) {
if(typeof this !== 'function') {
return
}
let _this = this
return function() {
return _this.apply(thisArg,...args);
}
};