規(guī)范解讀this:
基礎(chǔ)類型(base) | 引用類型(reference)
reference由三個(gè)組成部分豁状,分別是:
base value: 基礎(chǔ)值(null,undefined,string,number,boolean,symbol, or an environment record(環(huán)境記錄))其中的一種驼唱。
referenced name: 屬性名
strict reference: 嚴(yán)格模式
reference組成部分的方法:如GetBase和IsPropertyReference.
1.GetBase
返回reference的base balue
2.IsPropertyReference
如果base value是一個(gè)對(duì)象信卡,就返回true豌汇;
如何確定this的值:
讓我們描述一下:
1.計(jì)算 MemberExpression 的結(jié)果賦值給 ref
2.判斷 ref 是不是一個(gè) Reference 類型
2.1 如果 ref 是 Reference潮梯,并且 IsPropertyReference(ref) 是 true, 那么 this 的值為 GetBase(ref)
2.2 如果 ref 是 Reference靡羡,并且 base value 值是 Environment Record, 那么this的值為 ImplicitThisValue(ref)
2.3 如果 ref 不是 Reference贱鄙,那么 this 的值為 undefined
舉例:
舉最后一個(gè)例子:
var value = 1;
var foo = {
value: 2,
bar: function () {
return this.value;
}
}
//示例1
console.log(foo.bar());//1
//示例2
console.log((foo.bar)());//1, 因?yàn)閷?shí)際上()并沒(méi)有對(duì)它進(jìn)行計(jì)算
//示例3
console.log((foo.bar = foo.bar)());//2 .賦值的化會(huì)觸發(fā)GetValue方法扼劈,所以返回的值不是reference類型,那么this的值就是undefined蝌矛,在非嚴(yán)格模式下道批,this的值為undefined的時(shí)候,其值會(huì)被隱式地轉(zhuǎn)換為全局對(duì)象入撒。
//示例4
console.log((false || foo.bar)());//2 隆豹; 用了GetValue方法,返回的不是reference類型茅逮,this為undefined
//示例5
console.log((foo.bar, foo.bar)());//2璃赡, 逗號(hào)操作符,也 用了GetValue方法献雅,返回的不是reference類型碉考,this為undefined
補(bǔ)充:
function foo() {
console.log(this)
}
foo();
MemberExpression 是 foo,解析標(biāo)識(shí)符挺身,查看規(guī)范 10.3.1 Identifier Resolution侯谁,會(huì)返回一個(gè) Reference 類型的值:
var fooReference = {
base: EnvironmentRecord,
name: 'foo',
strict: false
};
因?yàn)?base value 是 EnvironmentRecord,并不是一個(gè) Object 類型章钾,還記得前面講過(guò)的 base value 的取值可能嗎墙贱? 只可能是 undefined, an Object, a Boolean, a String, a Number, 和 an environment record 中的一種。
IsPropertyReference(ref) 的結(jié)果為 false贱傀,進(jìn)入下個(gè)判斷:
2.2 如果 ref 是 Reference惨撇,并且 base value 值是 Environment Record, 那么this的值為 ImplicitThisValue(ref)
base value 正是 Environment Record,所以會(huì)調(diào)用 ImplicitThisValue(ref)
查看規(guī)范 10.2.1.1.6府寒,ImplicitThisValue 方法的介紹:該函數(shù)始終返回 undefined魁衙。
所以最后 this 的值就是 undefined。
模擬實(shí)現(xiàn)call:
call分為三步:
1.將函數(shù)設(shè)為對(duì)象的屬性 foo.fn = bar
2.執(zhí)行該函數(shù) foo.fn()
3.刪除該函數(shù) delete foo.fn;
eg:
var foo = {
value : 1
}
function bar () {
console.log(this.value)
}
bar.call(foo);
也就是:
var foo = {
value:1,
bar: function () {
console.log(this.value)
}
}
代碼實(shí)現(xiàn)
Function.prototype.myCall = function (obj) {//
//1.首先要獲取調(diào)用call的函數(shù)株搔,用this獲取
obj.fn = this;
//執(zhí)行函數(shù)
obj.fn();
//刪除刪除
delete obj.fn;
}
代碼實(shí)現(xiàn)第二步剖淀,傳參的時(shí)候
var foo = {
value: 1
};
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call(foo, 'kevin', 18);
理解:
arguments = {
0: foo,
1: 'kevin',
2: 18,
length: 3,
}
var argus = [];
for(var i = 1 , len = arguments.length ; i < len ; i ++) {
argus.push('arguments['+ i +']')
}
利用eval():參數(shù)是字符串,計(jì)算字符串并執(zhí)行計(jì)算出來(lái)的值
eval('obj.fn('+ argus +')')
Function.prototype.myCall = function (obj) {
obj.fn = this;
var argus = [];
for(var i = 1,len = obj.length; i < len; i++){
argus.push('obj['+i+']')
}
eval('obj.fn('+argus+')');
delete obj.fn;
}
最終版:當(dāng)指向null的時(shí)候邪狞,視為指向window
Function.prototype.call2 = function (obj) {
//沒(méi)有值的時(shí)候就指向window
var obj = obj || window;
obj.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
var result = eval('obj.fn(' + args +')');
delete obj.fn
return result;
}