11. 函数:内部特殊值和函数方法
- 罗列 this 值的常见情况。
另一个特殊的对象是 this。this 的值取决于多种因素。
(1) 全局上下文
this 在全局上下文中,即在任何函数体的外部时,都指向全局对象,在浏览器宿主环境中就是 window。
来看下面的例子:
let o1 = {this: this,o2: {this: [this],},};console.log(o1.this === window); // trueconsole.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);// -> falseconsole.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);// -> trueconsole.log(getThis.prototype.constructor() === getThis.prototype);// -> truelet 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()`的对象.```javascriptcolor = '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 = thisreturn function() {return _this.apply(thisArg,...args);}};
