一炮温、this 相關(guān)問(wèn)題
知乎上關(guān)于this的解答 this 的值到底是什么垒迂?一次說(shuō)清楚
this 的工作原理在js秘密花園中是這么解釋的
JavaScript 有一套完全不同于其它語(yǔ)言的對(duì) this 的處理機(jī)制。 在五種不同的情況下 悍引,this 指向的各不相同恩脂。
全局范圍內(nèi)
this;
當(dāng)在全部范圍內(nèi)使用this
,它將會(huì)指向全局對(duì)象趣斤。
函數(shù)調(diào)用
foo();
這里 this也會(huì)指向全局對(duì)象俩块。
ES5 注意: 在嚴(yán)格模式下(strict mode),不存在全局變量。 這種情況下 this將會(huì)是 undefined
方法調(diào)用
test.foo();
這個(gè)例子中玉凯,this
指向 test
對(duì)象势腮。
調(diào)用構(gòu)造函數(shù)
new foo();
如果函數(shù)傾向于和 new關(guān)鍵詞一塊使用,則我們稱這個(gè)函數(shù)是 構(gòu)造函數(shù)漫仆。 在函數(shù)內(nèi)部嫉鲸,this指向新創(chuàng)建的對(duì)象。
顯式的設(shè)置 this
function foo(a, b, c) {
}
var bar = {};
foo.apply(bar, [1, 2, 3]); // 數(shù)組將會(huì)被擴(kuò)展歹啼,如下所示
foo.call(bar, 1, 2, 3); // 傳遞到foo的參數(shù)是:a = 1, b = 2, c = 3
當(dāng)使用 Function.prototype
上的 call
或者 apply
方法時(shí)玄渗,函數(shù)內(nèi)的 this
將會(huì)被 顯式設(shè)置為函數(shù)調(diào)用的第一個(gè)參數(shù)。因此函數(shù)調(diào)用的規(guī)則在上例中已經(jīng)不適用了狸眼,在foo
函數(shù)內(nèi)this
被設(shè)置成了bar
藤树。
注意: 在對(duì)象的字面聲明語(yǔ)法中,this 不能用來(lái)指向?qū)ο蟊旧怼?因此 var obj = {me: this} 中的 me
不會(huì)指向 obj拓萌,因?yàn)?this 只可能出現(xiàn)在上述的五種情況中岁钓。 譯者注:這個(gè)例子中,如果是在瀏覽器中運(yùn)行微王,obj.me等于 window 對(duì)象屡限。
常見(jiàn)誤解
盡管大部分的情況都說(shuō)的過(guò)去,不過(guò)第一個(gè)規(guī)則(譯者注:這里指的應(yīng)該是第二個(gè)規(guī)則炕倘,也就是直接調(diào)用函數(shù)時(shí)钧大,this
指向全局對(duì)象) 被認(rèn)為是JavaScript語(yǔ)言另一個(gè)錯(cuò)誤設(shè)計(jì)的地方,因?yàn)樗?strong>從來(lái)就沒(méi)有實(shí)際的用途罩旋。
Foo.method = function() {
function test() {
// this 將會(huì)被設(shè)置為全局對(duì)象(譯者注:瀏覽器環(huán)境中也就是 window 對(duì)象)
}
test();
}
一個(gè)常見(jiàn)的誤解是 test
中的this
將會(huì)指向Foo
對(duì)象啊央,實(shí)際上不是這樣子的。為了在 test 中獲取對(duì) Foo對(duì)象的引用涨醋,我們需要在 method
函數(shù)內(nèi)部創(chuàng)建一個(gè)局部變量指向Foo
對(duì)象瓜饥。
Foo.method = function() {
var that = this;
function test() {
// 使用 that 來(lái)指向 Foo 對(duì)象
}
test();
}
that
只是我們隨意起的名字,不過(guò)這個(gè)名字被廣泛的用來(lái)指向外部的 this對(duì)象浴骂。
方法的賦值表達(dá)式
另一個(gè)看起來(lái)奇怪的地方是函數(shù)別名乓土,也就是將一個(gè)方法賦值給一個(gè)變量。
var test = someObject.methodTest;
test();
上例中溯警,test
就像一個(gè)普通的函數(shù)被調(diào)用趣苏;因此,函數(shù)內(nèi)的this
將不再被指向到someObject
對(duì)象愧膀。
雖然 this的晚綁定特性似乎并不友好拦键,但是這確實(shí)基于原型繼承賴以生存的土壤。
function Foo() {}
Foo.prototype.method = function() {};
function Bar() {}
Bar.prototype = Foo.prototype;
new Bar().method();
當(dāng) method被調(diào)用時(shí)檩淋,this將會(huì)指向 Bar的實(shí)例對(duì)象芬为。
1. apply萄金、call 有什么作用,什么區(qū)別
相同之處:
call與apply的作用媚朦,都是調(diào)用一個(gè)函數(shù)氧敢,傳入函數(shù)執(zhí)行上下文及參數(shù),第一個(gè)參數(shù)都是希望設(shè)置的this對(duì)象询张。
不同之處:
不同之處在于call方法接收若干個(gè)參數(shù)的列表孙乖,而apply接收一個(gè)包含多個(gè)參數(shù)的參數(shù)數(shù)組
Javascript的每個(gè)Function對(duì)象中有一個(gè)apply方法:
function.apply([thisObj[,argArray]])
還有一個(gè)類(lèi)似功能的call方法:
function.call([thisObj[,arg1[, arg2[, [,.argN]]]]])
它們各自的定義:
apply
:應(yīng)用某一對(duì)象的一個(gè)方法,用另一個(gè)對(duì)象替換當(dāng)前對(duì)象份氧。
call
:調(diào)用一個(gè)對(duì)象的一個(gè)方法唯袄,以另一個(gè)對(duì)象替換當(dāng)前對(duì)象。
它們的共同之處:
都“可以用來(lái)代替另一個(gè)對(duì)象調(diào)用一個(gè)方法蜗帜,將一個(gè)函數(shù)的對(duì)象上下文從初始的上下文改變?yōu)橛?thisObj 指定的新對(duì)象恋拷。
它們的不同之處:
apply
:
最多只能有兩個(gè)參數(shù)——新this對(duì)象和一個(gè)數(shù)組 argArray。如果給該方法傳遞多個(gè)參數(shù)厅缺,則把參數(shù)都寫(xiě)進(jìn)這個(gè)數(shù)組里面蔬顾,當(dāng)然,即使只有一個(gè)參數(shù)湘捎,也要寫(xiě)進(jìn)數(shù)組里面诀豁。如果 argArray 不是一個(gè)有效的數(shù)組或者不是 arguments 對(duì)象,那么將導(dǎo)致一個(gè) TypeError窥妇。如果沒(méi)有提供 argArray 和 thisObj 任何一個(gè)參數(shù)舷胜,那么 Global 對(duì)象將被用作 thisObj,并且無(wú)法被傳遞任何參數(shù)秩伞。
call
:
則是直接的參數(shù)列表逞带,主要用在js對(duì)象各方法互相調(diào)用的時(shí)候,使當(dāng)前this實(shí)例指針保持一致,或在特殊情況下需要改變this指針纱新。如果沒(méi)有提供 thisObj 參數(shù),那么 Global 對(duì)象被用作 thisObj穆趴。
更簡(jiǎn)單地說(shuō)脸爱,apply和call功能一樣,只是傳入的參數(shù)列表形式不同:如 func.call(func1,var1,var2,var3) 對(duì)應(yīng)的apply寫(xiě)法為:func.apply(func1,[var1,var2,var3])
也就是說(shuō):call調(diào)用的為單個(gè)未妹,apply調(diào)用的參數(shù)為數(shù)組
function sum(a,b){ console.log(this === window);//true console.log(a + b); } sum(1,2); sum.call(null,1,2); sum.apply(null,[1,2]);
作用
調(diào)用函數(shù)
var info = 'tom';
function foo(){
//this指向window
var info = 'jerry';
console.log(this.info); //tom
console.log(this===window) //true
}
foo();
foo.call();
foo.apply();
call和apply可以改變函數(shù)中this的指向
var obj = {
info:'spike'
}
foo.call(obj); //這里foo函數(shù)里面的this就指向了obj
foo.apply(obj)
借用別的對(duì)象的方法,求數(shù)組中的最大值
var arr = [123,34,5,23,3434,23];
方法一
var arr1 = arr.sort(function(a,b){
return b-a;
});
console.log(arr1[0]);
方法二
var max = Math.max.apply(null,arr) //借用別的對(duì)象的方法
console.log(max);
fn.call(context, param1, param2...)
fn.apply(context, paramArray)
2. 以下代碼輸出什么?
var john = {
firstName: "John"
}
function func() {
alert(this.firstName + ": hi!")
}
john.sayHi = func
john.sayHi()
輸出結(jié)果為'John: hi!'
簿废。這是由于john.sayHi = func
語(yǔ)句為對(duì)象john
新建了一個(gè)屬性,該屬性值為函數(shù)func
络它,當(dāng)函數(shù)被調(diào)用時(shí)族檬,此時(shí)的this
為對(duì)象john
本身。
3. 下面代碼輸出什么化戳,為什么
func()
function func() {
alert(this)
}
輸出為window
對(duì)象单料。由于func
是在全局作用域下被調(diào)用埋凯。
4. 下面代碼輸出什么
function fn0(){
function fn(){
console.log(this);
}
fn();
}
fn0();
document.addEventListener('click', function(e){
console.log(this);
setTimeout(function(){
console.log(this);
}, 200);
}, false);
第一個(gè)輸出window
,此時(shí)是全局變量調(diào)用的fn0
扫尖,第二個(gè)是document
白对,是DOM對(duì)象本身,第三個(gè)是window
换怖,這是由于setTimeout
函數(shù)是掛在window
對(duì)象下的甩恼。
5. 下面代碼輸出什么,why
var john = {
firstName: "John"
}
function func() {
alert( this.firstName )
}
func.call(john)
輸出'John'
沉颂,這是由于.call()
方法為函數(shù)指定了執(zhí)行環(huán)境条摸,在對(duì)象john
下。
6. 代碼輸出铸屉?
var john = {
firstName: "John",
surname: "Smith"
}
function func(a, b) {
alert( this[a] + ' ' + this[b] )
}
func.call(john, 'firstName', 'surname')
輸出'John Smith'
屈溉,這是由于func.call(john, 'firstName', 'surname')
為函數(shù)指定了執(zhí)行環(huán)境為john
對(duì)象,傳入?yún)?shù)分別為'firstName'抬探、'surname'
子巾,也就是取john
的這兩個(gè)屬性值,將其相加小压,得出結(jié)果降瞳。
7. 以下代碼有什么問(wèn)題,如何修改
var module= {
bind: function(){
$btn.on('click', function(){
console.log(this) //this指什么
this.showMsg();
})
},
showMsg: function(){
console.log('饑人谷');
}
}
bind屬性中梭姓,this.showMsg()語(yǔ)句的this指的是$btn忧换,是無(wú)法調(diào)用showMsg()的,此時(shí)需要保存事件綁定函數(shù)外部的this蜻牢,修改如下:
var module= {
bind: function(){
var cur = this
$btn.on('click', function(){
console.log(this) //this指什么
cur.showMsg();
})
},
showMsg: function(){
console.log('饑人谷');
}
}
二烤咧、原型鏈相關(guān)問(wèn)題
知乎上關(guān)于this的解答什么是 JS 原型鏈?
1. 有如下代碼抢呆,解釋Person煮嫌、 prototype、proto抱虐、p昌阿、constructor之間的關(guān)聯(lián)。
function Person(name){
this.name = name;
}
Person.prototype.sayName = function(){
console.log('My name is :' + this.name);
}
var p = new Person("若愚")
p.sayName();
person
是構(gòu)造函數(shù)恳邀;
prototype
是構(gòu)造函數(shù)的原型對(duì)象懦冰;
p
是構(gòu)造函數(shù)的實(shí)例;
__proto__
是p的原型鏈谣沸,指向person
的prototype
刷钢;
constructor
即構(gòu)造函數(shù)person
本身。
2. 上例中乳附,對(duì)對(duì)象 p可以這樣調(diào)用 p.toString()内地。toString是哪里來(lái)的? 畫(huà)出原型圖?并解釋什么是原型鏈伴澄。
toString來(lái)自object.prototype
每個(gè)對(duì)象有都有屬性prototype
對(duì)象都有屬性proto , 對(duì)象的proto指向創(chuàng)建他的對(duì)象的prototype,對(duì)象的prototype的proto又指向創(chuàng)建對(duì)象的prototype的對(duì)象的prototype瓤鼻,循環(huán)下去秉版,直到Object對(duì)象為止,因?yàn)镺bject.proto = null而這條鏈就是原型鏈
3. 對(duì)String做擴(kuò)展茬祷,實(shí)現(xiàn)如下方式獲取字符串中頻率最高的字符
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因?yàn)閐 出現(xiàn)了5次
//方法一:
String.prototype.getMostOften = function() {
var Str = [];
var num = [];
var include = false;
for(var i = 0; i < this.length; i++){
if(i === 0){
Str.push(this[i]);
num.push(0);
}
for(var key in Str){
if(Str[key] === this[i]){
num[key]++;
include = true;
}
}
if(!include){
Str.push(this[i]);
num.push(1);
}
include = false;
}
var index = num.indexOf(Math.max.apply(Math, num));
return Str[index];
}
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因?yàn)閐 出現(xiàn)了5次
//方法二:
String.prototype.getMostOften = function() {
var Str = {};
var key ;
for(var i = 0; i < this.length; i++){
key = this[i];
if(!Str[key]){
Str[key] = 1;
}
else{
Str[key] ++;
}
}
var max = 0;
var strKey;
for(var k in Str){
if(Str[k] > max){
max = Str[k];
strKey = k;
}
}
return strKey;
}
var str = 'ahbbccdeddddfg';
var ch = str.getMostOften();
console.log(ch); //d , 因?yàn)閐 出現(xiàn)了5次
4. instanceOf有什么作用清焕??jī)?nèi)部邏輯是如何實(shí)現(xiàn)的?
作用:判斷對(duì)象在其原型鏈中是否存在一個(gè)構(gòu)造函數(shù)的prototype屬性
function isInstanceOf(obj, fn){
var oldpro = obj.__proto__;
do{
if(oldpro === fn.prototype){
return true;
}
else {
oldpro = oldpro.__proto__;
}
}while(oldpro)
return false;
}
三祭犯、繼承相關(guān)問(wèn)題
1. 繼承有什么作用?
1. 子類(lèi)擁有父類(lèi)的屬性和方法秸妥,不需要重復(fù)寫(xiě)代碼,修改時(shí)也只需修改一份代碼
2. 可以重寫(xiě)和擴(kuò)展父類(lèi)的屬性和代碼沃粗,又不影響父類(lèi)本身
2.下面兩種寫(xiě)法有什么區(qū)別?
//方法1
function People(name, sex){
this.name = name;
this.sex = sex;
this.printName = function(){
console.log(this.name);
}
}
var p1 = new People('饑人谷', 2)
//方法2
function Person(name, sex){
this.name = name;
this.sex = sex;
}
Person.prototype.printName = function(){
console.log(this.name);
}
var p1 = new Person('若愚', 27);
方法一的寫(xiě)法是屬性方法都寫(xiě)入p1中粥惧;方法二中的p1只有屬性name和sex,方法綁定在Person.prototype屬性下最盅,p1可以繼承父類(lèi)的屬性和方法突雪。這樣做的好處是節(jié)約代碼量,提高性能涡贱。
3. Object.create 有什么作用咏删?兼容性如何?
Object.create(proto[, propertiesObject ])
創(chuàng)建一個(gè)擁有指定原型和若干個(gè)指定屬性的對(duì)象
proto:一個(gè)對(duì)象问词,作為新創(chuàng)建對(duì)象的原型督函。或者為null激挪。
propertiesObject:可選辰狡。該參數(shù)對(duì)象是一組屬性與值,該對(duì)象的屬性名稱將是新創(chuàng)建的對(duì)象的屬性名稱垄分,值是屬性描述符(這些屬性描述符的結(jié)構(gòu)與Object.defineProperties()的第二個(gè)參數(shù)一樣)宛篇。注意:該參數(shù)對(duì)象不能是 undefined,另外只有該對(duì)象中自身?yè)碛械目擅杜e的屬性才有效锋喜,也就是說(shuō)該對(duì)象的原型鏈上屬性是無(wú)效的些己。
4. hasOwnProperty有什么作用? 如何使用嘿般?
js秘密花園對(duì)hasOwnProperty的解釋
hasOwnProperty函數(shù)
為了判斷一個(gè)對(duì)象是否包含自定義屬性而不是原型鏈上的屬性, 我們需要使用繼承自 Object.prototype
的 hasOwnProperty
方法涯冠。
注意: 通過(guò)判斷一個(gè)屬性是否 undefined是不夠的炉奴。 因?yàn)橐粋€(gè)屬性可能確實(shí)存在,只不過(guò)它的值被設(shè)置為 undefined蛇更。
hasOwnProperty
是 JavaScript 中唯一一個(gè)處理屬性但是不查找原型鏈的函數(shù)瞻赶。
// 修改Object.prototypeObject.prototype.bar = 1;
var foo = {goo: undefined};
foo.bar; // 1
'bar' in foo; // true
foo.hasOwnProperty('bar'); // false
foo.hasOwnProperty('goo'); // true
只有 hasOwnProperty
可以給出正確和期望的結(jié)果赛糟,這在遍歷對(duì)象的屬性時(shí)會(huì)很有用砸逊。 沒(méi)有其它方法可以用來(lái)排除原型鏈上的屬性璧南,而不是定義在對(duì)象自身上的屬性师逸。
hasOwnProperty作為屬性JavaScript 不會(huì)保護(hù)hasOwnProperty
被非法占用司倚,因此如果一個(gè)對(duì)象碰巧存在這個(gè)屬性动知, 就需要使用外部的 hasOwnProperty
函數(shù)來(lái)獲取正確的結(jié)果。
var foo = {
hasOwnProperty: function() {
return false;
},
bar: 'Here be dragons'
};
foo.hasOwnProperty('bar'); // 總是返回 false // 使用其它對(duì)象的 hasOwnProperty,并將其上下為設(shè)置為foo
{}.hasOwnProperty.call(foo, 'bar'); // true
結(jié)論
當(dāng)檢查對(duì)象上某個(gè)屬性是否存在時(shí)奠滑,hasOwnProperty 是唯一可用的方法丹皱。 同時(shí)在使用 for in loop 遍歷對(duì)象時(shí),推薦總是使用 hasOwnProperty方法宋税, 這將會(huì)避免原型對(duì)象擴(kuò)展帶來(lái)的干擾
5. 如下代碼中call的作用是什么?
function Person(name, sex){
this.name = name;
this.sex = sex;
}
function Male(name, sex, age){
Person.call(this, name, sex); //這里的 call 有什么作用
this.age = age;
}
call調(diào)用Person方法摊崭,指定Person方法中的this為Male,并傳入?yún)?shù)sex弃甥,age
6. 補(bǔ)全代碼爽室,實(shí)現(xiàn)繼承
function Person(name, sex){
// todo ...
}
Person.prototype.getName = function(){
// todo ...
};
function Male(name, sex, age){
//todo ...
}
//todo ...
Male.prototype.getAge = function(){
//todo ...
};
代碼如下:
var ruoyu = new Male('若愚', '男', 27);
ruoyu.printName();
function Person(name, sex){
this.name = name;
this.sex = sex;
this.printName = function(){
console.log(name)
}
}
Person.prototype.getName = function(){
console.log(this.name)
};
function Male(name, sex, age){
Person.call(this, name, sex);
this.age = age;
}
Male.prototype = Object.create(Person.prototype)
Male.prototype.getAge = function(){
console.log(this.age)
};
var ruoyu = new Male('若愚', '男', 27);
ruoyu.printName();
ruoyu.getAge();
ruoyu.getName();