javascript中的原型鏈
要弄清楚原型鏈就要先弄清楚 function 類(lèi)型,在javascript中沒(méi)有類(lèi)的概念,都是函數(shù),所以它是一門(mén)函數(shù)式的編程語(yǔ)言。類(lèi)有一個(gè)很重要的特性,就是它可以根據(jù)它的構(gòu)造函數(shù)來(lái)創(chuàng)建以它為模板的對(duì)象。在javascript中,函數(shù)就有2個(gè)功能
第一、 作為一般函數(shù)調(diào)用
第二、 作為它原型對(duì)象的構(gòu)造函數(shù) 也就new()
我們來(lái)看一個(gè)例子
?。踛avascript] view plain copyfunction a(){
this.name = ‘a(chǎn)’;
}
當(dāng)創(chuàng)建一個(gè)函數(shù),它會(huì)發(fā)生什么呢?
第一、它會(huì)創(chuàng)建1個(gè)函數(shù)對(duì)象 也就是a 本身
第二、它會(huì)創(chuàng)建1個(gè)原型對(duì)象@a(用@來(lái)表示)
第三、函數(shù)對(duì)象會(huì)有一個(gè)prototype指針,它指向了對(duì)應(yīng)的原型對(duì)象,這里就指向了@a
第四、@a對(duì)象中有一個(gè)construtor指針,指向它的構(gòu)造函數(shù),這里就指向了a
這個(gè)prototype屬性究竟有什么用呢?
其實(shí)prototype 屬性表示當(dāng)前函數(shù)能夠控制的范圍(或者說(shuō)它指明了當(dāng)前函數(shù)是誰(shuí)的構(gòu)造函數(shù)),這里a就是@a原型對(duì)象的構(gòu)造函數(shù),所以我們會(huì)看見(jiàn)有這種寫(xiě)法
[javascript] view plain copyfunction a(){
this.name = ‘a(chǎn)’;
}
var a1 = new a();
這就和其他常見(jiàn)語(yǔ)言相似了,new 就是調(diào)用原型對(duì)象(通過(guò)prototype指針)里面構(gòu)造函數(shù)(constructor)創(chuàng)建一個(gè)新的對(duì)象實(shí)例。
那么修改了prototype指向?qū)ο罄锩娴膶傩?,也就影響了所有以它為模板?chuàng)建的實(shí)例,我們可以這樣來(lái)驗(yàn)證
?。踛avascript] view plain copyfunction a(){
this.name = ‘a(chǎn)’;
}
var a1 = new a();
a.prototype.age = 1;
alert(a1.age);
結(jié)果:1
那為什么a1對(duì)象可以直接訪問(wèn)到age屬性呢?a1對(duì)象里面我并沒(méi)有定義age屬性啊,
那是因?yàn)樗袑?shí)例里面都會(huì)有一個(gè)引用_proto_(在firfox,chrome下可以直接訪問(wèn),ie不支持)指向了這個(gè)原型,這里就是指向了@a,
[javascript] view plain copyfunction a(){
this.name = ‘a(chǎn)’;
}
var a1 = new a();
alert(a1._proto_ == a.prototype)
結(jié)果:true
在訪問(wèn)屬性的時(shí)候,會(huì)先在a1對(duì)象內(nèi)部中尋找,如果沒(méi)有,就會(huì)順著_proto_指向的對(duì)象里面去尋找,這里會(huì)到@a中尋找,找到就返回值,沒(méi)有找到就返回undefined,用個(gè)成語(yǔ)來(lái)形容,就是順藤摸瓜嘛!
至此原型鏈的含義就出來(lái)了,由于原型對(duì)象也有一個(gè)_proto_指針,又指向了另一個(gè)原型,一個(gè)接一個(gè),就形成了原型鏈。Object.prototype是最頂層的原型,所以如果修改了Object.prototype的屬性,那么就影響了所有的對(duì)象。
在來(lái)看一段代碼
?。踛avascript] view plain copyfunction a(){
this.name = ‘a(chǎn)’;
}
function b(){
this.age = 1;
}
b.prototype = new a();
alert(new b().name);
我們顯示的將b的原型指向了a的一個(gè)實(shí)例,然后,b的實(shí)例也可以訪問(wèn)a的屬性了。這就是javascript的繼承了,那為什么b.prototype 指向的是a的一個(gè)實(shí)例,而不是直接指向a.prototype 呢?
[javascript] view plain copyb.prototype = new a.prototype;
如果像上面這么寫(xiě),修改p.prototype中的屬性,那么a的原型也會(huì)改變了,相當(dāng)于是子類(lèi)修改了父類(lèi),并且子類(lèi)和父類(lèi)的屬性糅合在了一起,這顯然是不合適的。換句話說(shuō),b也成為了@a的構(gòu)造函數(shù),a,b成了平級(jí)的關(guān)系。
我們可以下一個(gè)定義:
函數(shù)a 繼承函數(shù)b 也就是讓函數(shù)a成為函數(shù)b原型的一個(gè)實(shí)例的構(gòu)造函數(shù),構(gòu)造函數(shù)里面聲明的屬性是函數(shù)a自己的,原型實(shí)例里面的屬性就是繼承b的
[javascript] view plain copyvar $ = jQuery = function(selector,context){
//不可能在自己的構(gòu)造函數(shù)中又一次構(gòu)造自己,所以返回了另外一個(gè)構(gòu)造函數(shù)的實(shí)例
return new init(selector,context);
}
jQuery.fn = jQuery.prototype = {
size:function(){
return this.length;
}
}
function init (selector,context){
}
init.prototype = jQuery.fn;;
}
這是jquery的一段源碼,我們?cè)谑褂胘query的時(shí)候,并沒(méi)有使用new關(guān)鍵字,那它是如何構(gòu)造對(duì)象的呢?
用上面的知識(shí),可以解釋?zhuān)琷query這里只是一個(gè)一般函數(shù)的調(diào)用,它返回了jquery原型的另外一個(gè)構(gòu)造函數(shù)創(chuàng)建的對(duì)象,也就是new init()
JavaScript中的原型
原型是JavaScript中一個(gè)比較難理解的概念,原型相關(guān)的屬性也比較多,對(duì)象有“[[prototype]]”屬性,函數(shù)對(duì)象有“prototype”屬性,原型對(duì)象有“constructor”屬性。
認(rèn)識(shí)原型
開(kāi)始原型的介紹之前,首先來(lái)認(rèn)識(shí)一下什么是原型?
在JavaScript中,原型也是一個(gè)對(duì)象,通過(guò)原型可以實(shí)現(xiàn)對(duì)象的屬性繼承,JavaScript的對(duì)象中都包含了一個(gè)“ [[Prototype]]”內(nèi)部屬性,這個(gè)屬性所對(duì)應(yīng)的就是該對(duì)象的原型。
“[[Prototype]]”作為對(duì)象的內(nèi)部屬性,是不能被直接訪問(wèn)的。所以為了方便查看一個(gè)對(duì)象的原型,F(xiàn)irefox和Chrome中提供了“__proto__”這個(gè)非標(biāo)準(zhǔn)(不是所有瀏覽器都支持)的訪問(wèn)器(ECMA引入了標(biāo)準(zhǔn)對(duì)象原型訪問(wèn)器“Object.getPrototype(object)”)。
實(shí)例分析
下面通過(guò)一個(gè)例子來(lái)看看原型相關(guān)概念:
function Person(name, age){
this.name = name;
this.age = age;
this.getInfo = function(){
console.log(this.name + “ is ” + this.age + “ years old”);
};
}
var will = new Person(“Will”, 28);
在上面的代碼中,通過(guò)了Person這個(gè)構(gòu)造函數(shù)創(chuàng)建了一個(gè)will對(duì)象。下面就通過(guò)will這個(gè)對(duì)象一步步展開(kāi)了解原型。
Step 1: 查看對(duì)象will的原型
通過(guò)下面代碼,可以查看對(duì)象will的原型:
console.log(will.__proto__);
console.log(will.constructor);
結(jié)果分析:
“Person {}”對(duì)象就是對(duì)象will的原型,通過(guò)Chrome展開(kāi)可以看到,“Person {}”作為一個(gè)原型對(duì)象,也有“__proto__”屬性(對(duì)應(yīng)原型的原型)。
在這段代碼中,還用到了“constructor”屬性。在JavaScript的原型對(duì)象中,還包含一個(gè)“constructor”屬性,這個(gè)屬性對(duì)應(yīng)創(chuàng)建所有指向該原型的實(shí)例的構(gòu)造函數(shù)。
通過(guò)“constructor”這個(gè)屬性,我們可以來(lái)判斷一個(gè)對(duì)象是不是數(shù)組類(lèi)型
function isArray(myArray) {
return myArray.constructor.toString().indexOf(“Array”) 》 -1;
}
在這里,will對(duì)象本身并沒(méi)有“constructor”這個(gè)屬性,但是通過(guò)原型鏈查找,找到了will原型(will.__proto__)的“constructor”屬性,并得到了Person函數(shù)。
Step 2: 查看對(duì)象will的原型(will.__proto__)的原型
既然will的原型“Person {}”也是一個(gè)對(duì)象,那么我們就同樣可以來(lái)查看“will的原型(will.__proto__)的原型”。
運(yùn)行下面的代碼:
console.log(will.__proto__ === Person.prototype);
console.log(Person.prototype.__proto__);
console.log(Person.prototype.constructor);
console.log(Person.prototype.constructor === Person);
結(jié)果分析:
首先看 “will.__proto__ === Person.prototype”,在JavaScript中,每個(gè)函數(shù)都有一個(gè)prototype屬性,當(dāng)一個(gè)函數(shù)被用作構(gòu)造函數(shù)來(lái)創(chuàng)建實(shí)例時(shí),該函數(shù)的prototype屬性值將被作為原型賦值給所有對(duì)象實(shí)例(也就是設(shè)置實(shí)例的__proto__屬性),也就是說(shuō),所有實(shí)例的原型引用的是函數(shù)的prototype屬性。了解了構(gòu)造函數(shù)的prototype屬性之后,一定就明白為什么第一句結(jié)果為true了。
prototype屬性是函數(shù)對(duì)象特有的,如果不是函數(shù)對(duì)象,將不會(huì)有這樣一個(gè)屬性。
當(dāng)通過(guò)“Person.prototype.__proto__”語(yǔ)句獲取will對(duì)象原型的原型時(shí)候,將得到“Object {}”對(duì)象,后面將會(huì)看到所有對(duì)象的原型都將追溯到“Object {}”對(duì)象。
對(duì)于原型對(duì)象“Person.prototype”的“constructor”,根據(jù)前面的介紹,將對(duì)應(yīng)Person函數(shù)本身。
通過(guò)上面可以看到,“Person.prototype”對(duì)象和Person函數(shù)對(duì)象通過(guò)“constructor”和“prototype”屬性實(shí)現(xiàn)了相互引用(后面會(huì)有圖展示這個(gè)相互引用的關(guān)系)。
Step 3: 查看對(duì)象Object的原型
通過(guò)前一部分可以看到,will的原型的原型是“Object {}”對(duì)象。實(shí)際上在JavaScript中,所有對(duì)象的原型都將追溯到“Object {}”對(duì)象。
下面通過(guò)一段代碼看看“Object {}”對(duì)象:
console.log(Person.prototype.__proto__ === Object.prototype);
console.log(typeof Object);
console.log(Object);
console.log(Object.prototype);
console.log(Object.prototype.__proto__);
console.log(Object.prototype.constructor);
通過(guò)下面的代碼可以看到:
Object對(duì)象本身是一個(gè)函數(shù)對(duì)象。
既然是Object函數(shù),就肯定會(huì)有prototype屬性,所以可以看到“Object.prototype”的值就是“Object {}”這個(gè)原型對(duì)象。
反過(guò)來(lái),當(dāng)訪問(wèn)“Object.prototype”對(duì)象的“constructor”這個(gè)屬性的時(shí)候,就得到了Obejct函數(shù)。
另外,當(dāng)通過(guò)“Object.prototype.__proto__”獲取Object原型的原型的時(shí)候,將會(huì)得到“null”,也就是說(shuō)“Object {}”原型對(duì)象就是原型鏈的終點(diǎn)了。
Step 4: 查看對(duì)象Function的原型
在上面的例子中,Person是一個(gè)構(gòu)造函數(shù),在JavaScript中函數(shù)也是對(duì)象,所以,我們也可以通過(guò)“__proto__”屬性來(lái)查找Person函數(shù)對(duì)象的原型。
console.log(Person.__proto__ === Function.prototype);
console.log(Person.constructor === Function)
console.log(typeof Function);
console.log(Function);
console.log(Function.prototype);
console.log(Function.prototype.__proto__);
console.log(Function.prototype.constructor);
結(jié)果分析 :
在JavaScript中有個(gè)Function對(duì)象(類(lèi)似Object),這個(gè)對(duì)象本身是個(gè)函數(shù);所有的函數(shù)(包括Function,Object)的原型(__proto__)都是“Function.prototype”。
Function對(duì)象作為一個(gè)函數(shù),就會(huì)有prototype屬性,該屬性將對(duì)應(yīng)“function () {}”對(duì)象。
Function對(duì)象作為一個(gè)對(duì)象,就有“__proto__”屬性,該屬性對(duì)應(yīng)“Function.prototype”,也就是說(shuō),“Function.__proto__ === Function.prototype”
對(duì)于Function的原型對(duì)象“Function.prototype”,該原型對(duì)象的“__proto__”屬性將對(duì)應(yīng)“Object {}”
對(duì)比prototype和__proto__
對(duì)于“prototype”和“__proto__”這兩個(gè)屬性有的時(shí)候可能會(huì)弄混,“Person.prototype”和“Person.__proto__”是完全不同的。
在這里對(duì)“prototype”和“__proto__”進(jìn)行簡(jiǎn)單的介紹:
對(duì)于所有的對(duì)象,都有__proto__屬性,這個(gè)屬性對(duì)應(yīng)該對(duì)象的原型
對(duì)于函數(shù)對(duì)象,除了__proto__屬性之外,還有prototype屬性,當(dāng)一個(gè)函數(shù)被用作構(gòu)造函數(shù)來(lái)創(chuàng)建實(shí)例時(shí),該函數(shù)的prototype屬性值將被作為原型賦值給所有對(duì)象實(shí)例(也就是設(shè)置實(shí)例的__proto__屬性)
圖解實(shí)例
通過(guò)上面結(jié)合實(shí)例的分析,相信你一定了解了原型中的很多內(nèi)容。
但是現(xiàn)在肯定對(duì)上面例子中的關(guān)系感覺(jué)很凌亂,一會(huì)兒原型,一會(huì)兒原型的原型,還有Function,Object,constructor,prototype等等關(guān)系。
現(xiàn)在就對(duì)上面的例子中分析得到的結(jié)果/關(guān)系進(jìn)行圖解,相信這張圖可以讓你豁然開(kāi)朗。
對(duì)于上圖的總結(jié)如下:
所有的對(duì)象都有“__proto__”屬性,該屬性對(duì)應(yīng)該對(duì)象的原型
所有的函數(shù)對(duì)象都有“prototype”屬性,該屬性的值會(huì)被賦值給該函數(shù)創(chuàng)建的對(duì)象的“__proto__”屬性
所有的原型對(duì)象都有“constructor”屬性,該屬性對(duì)應(yīng)創(chuàng)建所有指向該原型的實(shí)例的構(gòu)造函數(shù)
函數(shù)對(duì)象和原型對(duì)象通過(guò)“prototype”和“constructor”屬性進(jìn)行相互關(guān)聯(lián)
通過(guò)原型改進(jìn)例子
在上面例子中,“getInfo”方法是構(gòu)造函數(shù)Person的一個(gè)成員,當(dāng)通過(guò)Person構(gòu)造兩個(gè)實(shí)例的時(shí)候,每個(gè)實(shí)例都會(huì)包含一個(gè)“getInfo”方法。
var will = new Person(“Will”, 28);
var wilber = new Person(“Wilber”, 27);
前面了解到,原型就是為了方便實(shí)現(xiàn)屬性的繼承,所以可以將“getInfo”方法當(dāng)作Person原型(Person.__proto__)的一個(gè)屬性,這樣所有的實(shí)例都可以通過(guò)原型繼承的方式來(lái)使用“getInfo”這個(gè)方法了。
所以對(duì)例子進(jìn)行如下修改:
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.getInfo = function(){
console.log(this.name + “ is ” + this.age + “ years old”);
};
修改后的結(jié)果為:
原型鏈
因?yàn)槊總€(gè)對(duì)象和原型都有原型,對(duì)象的原型指向?qū)ο蟮母福傅脑陀种赶蚋傅母?,這種原型層層連接起來(lái)的就構(gòu)成了原型鏈。
在“理解JavaScript的作用域鏈”一文中,已經(jīng)介紹了標(biāo)識(shí)符和屬性通過(guò)作用域鏈和原型鏈的查找。
這里就繼續(xù)看一下基于原型鏈的屬性查找。
屬性查找
當(dāng)查找一個(gè)對(duì)象的屬性時(shí),JavaScript 會(huì)向上遍歷原型鏈,直到找到給定名稱(chēng)的屬性為止,到查找到達(dá)原型鏈的頂部(也就是 “Object.prototype”), 如果仍然沒(méi)有找到指定的屬性,就會(huì)返回 undefined。
看一個(gè)例子:
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.MaxNumber = 9999;
Person.__proto__.MinNumber = -9999;
var will = new Person(“Will”, 28);
console.log(will.MaxNumber);
// 9999
console.log(will.MinNumber);
// undefined
在這個(gè)例子中分別給“Person.prototype ”和“ Person.__proto__”這兩個(gè)原型對(duì)象添加了“MaxNumber ”和“MinNumber”屬性,這里就需要弄清“prototype”和“__proto__”的區(qū)別了。
“Person.prototype ”對(duì)應(yīng)的就是Person構(gòu)造出來(lái)所有實(shí)例的原型,也就是說(shuō)“Person.prototype ”屬于這些實(shí)例原型鏈的一部分,所以當(dāng)這些實(shí)例進(jìn)行屬性查找時(shí)候,就會(huì)引用到“Person.prototype ”中的屬性。
屬性隱藏
當(dāng)通過(guò)原型鏈查找一個(gè)屬性的時(shí)候,首先查找的是對(duì)象本身的屬性,如果找不到才會(huì)繼續(xù)按照原型鏈進(jìn)行查找。
這樣一來(lái),如果想要覆蓋原型鏈上的一些屬性,我們就可以直接在對(duì)象中引入這些屬性,達(dá)到屬性隱藏的效果。
看一個(gè)簡(jiǎn)單的例子:
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.getInfo = function(){
console.log(this.name + “ is ” + this.age + “ years old”);
};
var will = new Person(“Will”, 28);
will.getInfo = function(){
console.log(“getInfo method from will instead of prototype”);
};
will.getInfo();
// getInfo method from will instead of prototype
對(duì)象創(chuàng)建方式影響原型鏈
會(huì)到本文開(kāi)始的例子,will對(duì)象通過(guò)Person構(gòu)造函數(shù)創(chuàng)建,所以will的原型(will.__proto__)就是“Person.prototype”。
同樣,我們可以通過(guò)下面的方式創(chuàng)建一個(gè)對(duì)象:
var July = {
name: “July”,
age: 28,
getInfo: function(){
console.log(this.name + “ is ” + this.age + “ years old”);
},
}
console.log(July.getInfo());
當(dāng)使用這種方式創(chuàng)建一個(gè)對(duì)象的時(shí)候,原型鏈就變成下圖了,July對(duì)象的原型是“Object.prototype”也就是說(shuō)對(duì)象的構(gòu)建方式會(huì)影響原型鏈的形式。
hasOwnProperty
“hasOwnProperty”是“Object.prototype”的一個(gè)方法,該方法能判斷一個(gè)對(duì)象是否包含自定義屬性而不是原型鏈上的屬性,因?yàn)椤癶asOwnProperty” 是 JavaScript 中唯一一個(gè)處理屬性但是不查找原型鏈的函數(shù)。
相信你還記得文章最開(kāi)始的例子中,通過(guò)will我們可以訪問(wèn)“constructor”這個(gè)屬性,并得到will的構(gòu)造函數(shù)Person。這里結(jié)合“hasOwnProperty”這個(gè)函數(shù)就可以看到,will對(duì)象并沒(méi)有“constructor”這個(gè)屬性。
從下面的輸出可以看到,“constructor”是will的原型(will.__proto__)的屬性,但是通過(guò)原型鏈的查找,will對(duì)象可以發(fā)現(xiàn)并使用“constructor”屬性。
“hasOwnProperty”還有一個(gè)重要的使用場(chǎng)景,就是用來(lái)遍歷對(duì)象的屬性。
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.getInfo = function(){
console.log(this.name + “ is ” + this.age + “ years old”);
};
var will = new Person(“Will”, 28);
for(var attr in will){
console.log(attr);
}
// name
// age
// getInfo
for(var attr in will){
if(will.hasOwnProperty(attr)){
console.log(attr);
}
}
// name
// age
javascript原型和原型鏈有什么特點(diǎn)
每個(gè)對(duì)象都會(huì)在其內(nèi)部初始化一個(gè)屬性,就是prototype(原型),當(dāng)我們?cè)L問(wèn)一個(gè)對(duì)象的屬性時(shí),
如果這個(gè)對(duì)象內(nèi)部不存在這個(gè)屬性,那么他就會(huì)去prototype里找這個(gè)屬性,這個(gè)prototype又會(huì)有自己的prototype,
于是就這樣一直找下去,也就是我們平時(shí)所說(shuō)的原型鏈的概念。
關(guān)系:instance.constructor.prototype = instance.__proto__
特點(diǎn):
JavaScript對(duì)象是通過(guò)引用來(lái)傳遞的,我們創(chuàng)建的每個(gè)新對(duì)象實(shí)體中并沒(méi)有一份屬于自己的原型副本。當(dāng)我們修改原型時(shí),與之相關(guān)的對(duì)象也會(huì)繼承這一改變。
當(dāng)我們需要一個(gè)屬性的時(shí),Javascript引擎會(huì)先看當(dāng)前對(duì)象中是否有這個(gè)屬性, 如果沒(méi)有的話,
就會(huì)查找他的Prototype對(duì)象是否有這個(gè)屬性,如此遞推下去,一直檢索到 Object 內(nèi)建對(duì)象。
function Func(){}
Func.prototype.name = “Sean”;
Func.prototype.getInfo = function() {
return this.name;
}
var person = new Func();//現(xiàn)在可以參考var person = Object.create(oldObject);
console.log(person.getInfo());//它擁有了Func的屬性和方法
//“Sean”
console.log(Func.prototype);
// Func { name=“Sean”, getInfo=function()}
寫(xiě)一個(gè)通用的事件偵聽(tīng)器函數(shù)。
// event(事件)工具集,來(lái)源:github.com/markyun
markyun.Event = {
// 頁(yè)面加載完成后
readyEvent : function(fn) {
if (fn==null) {
fn=document;
}
var oldonload = window.onload;
if (typeof window.onload != ‘function’) {
window.onload = fn;
} else {
window.onload = function() {
oldonload();
fn();
};
}
},
// 視能力分別使用dom0||dom2||IE方式 來(lái)綁定事件
// 參數(shù): 操作的元素,事件名稱(chēng) ,事件處理程序
addEvent : function(element, type, handler) {
if (element.addEventListener) {
//事件類(lèi)型、需要執(zhí)行的函數(shù)、是否捕捉
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent(‘on’ + type, function() {
handler.call(element);
});
} else {
element[‘on’ + type] = handler;
}
},
// 移除事件
removeEvent : function(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if (element.datachEvent) {
element.detachEvent(‘on’ + type, handler);
} else {
element[‘on’ + type] = null;
}
},
// 阻止事件 (主要是事件冒泡,因?yàn)镮E不支持事件捕獲)
stopPropagation : function(ev) {
if (ev.stopPropagation) {
ev.stopPropagation();
} else {
ev.cancelBubble = true;
}
},
// 取消事件的默認(rèn)行為
preventDefault : function(event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
},
// 獲取事件目標(biāo)
getTarget : function(event) {
return event.target || event.srcElement;
},
// 獲取event對(duì)象的引用,取到事件的所有信息,確保隨時(shí)能使用event;
getEvent : function(e) {
var ev = e || window.event;
if (!ev) {
var c = this.getEvent.caller;
while (c) {
ev = c.arguments[0];
if (ev && Event == ev.constructor) {
break;
}
c = c.caller;
}
}
return ev;
}
};
對(duì)Node的優(yōu)點(diǎn)和缺點(diǎn)提出了自己的看法?
*(優(yōu)點(diǎn))因?yàn)镹ode是基于事件驅(qū)動(dòng)和無(wú)阻塞的,所以非常適合處理并發(fā)請(qǐng)求,
因此構(gòu)建在Node上的代理服務(wù)器相比其他技術(shù)實(shí)現(xiàn)(如Ruby)的服務(wù)器表現(xiàn)要好得多。
此外,與Node代理服務(wù)器交互的客戶(hù)端代碼是由javascript語(yǔ)言編寫(xiě)的,
因此客戶(hù)端和服務(wù)器端都用同一種語(yǔ)言編寫(xiě),這是非常美妙的事情。
*(缺點(diǎn))Node是一個(gè)相對(duì)新的開(kāi)源項(xiàng)目,所以不太穩(wěn)定,它總是一直在變,
而且缺少足夠多的第三方庫(kù)支持??雌饋?lái),就像是Ruby/Rails當(dāng)年的樣子。
Javascript如何實(shí)現(xiàn)繼承?
1、構(gòu)造繼承
2、原型繼承
3、實(shí)例繼承
4、拷貝繼承
原型prototype機(jī)制或apply和call方法去實(shí)現(xiàn)較簡(jiǎn)單,建議使用構(gòu)造函數(shù)與原型混合方式。
function Parent(){
this.name = ‘wang’;
}
function Child(){
this.age = 28;
}
Child.prototype = new Parent();//繼承了Parent,通過(guò)原型
var demo = new Child();
alert(demo.age);
alert(demo.name);//得到被繼承的屬性
}
Javascript作用鏈域?
全局函數(shù)無(wú)法查看局部函數(shù)的內(nèi)部細(xì)節(jié),但局部函數(shù)可以查看其上層的函數(shù)細(xì)節(jié),直至全局細(xì)節(jié)。
當(dāng)需要從局部函數(shù)查找某一屬性或方法時(shí),如果當(dāng)前作用域沒(méi)有找到,就會(huì)上溯到上層作用域查找,
直至全局函數(shù),這種組織形式就是作用域鏈。
javascript創(chuàng)建對(duì)象的幾種方式?
javascript創(chuàng)建對(duì)象簡(jiǎn)單的說(shuō),無(wú)非就是使用內(nèi)置對(duì)象或各種自定義對(duì)象,當(dāng)然還可以用JSON;但寫(xiě)法有很多種,也能混合使用。
1、對(duì)象字面量的方式
person={firstname:“Mark”,lastname:“Yun”,age:25,eyecolor:“black”};
2、用function來(lái)模擬無(wú)參的構(gòu)造函數(shù)
function Person(){}
var person=new Person();//定義一個(gè)function,如果使用new“實(shí)例化”,該function可以看作是一個(gè)Class
person.name=“Mark”;
person.age=“25”;
person.work=function(){
alert(person.name+“ hello.。。”);
}
person.work();
3、用function來(lái)模擬參構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)(用this關(guān)鍵字定義構(gòu)造的上下文屬性)
function Pet(name,age,hobby){
this.name=name;//this作用域:當(dāng)前對(duì)象
this.age=age;
this.hobby=hobby;
this.eat=function(){
alert(“我叫”+this.name+“,我喜歡”+this.hobby+“,是個(gè)程序員”);
}
}
var maidou =new Pet(“麥兜”,25,“coding”);//實(shí)例化、創(chuàng)建對(duì)象
maidou.eat();//調(diào)用eat方法
4、用工廠方式來(lái)創(chuàng)建(內(nèi)置對(duì)象)
var wcDog =new Object();
wcDog.name=“旺財(cái)”;
wcDog.age=3;
wcDog.work=function(){
alert(“我是”+wcDog.name+“,汪汪汪。。。。。?!保?
}
wcDog.work();
5、用原型方式來(lái)創(chuàng)建
function Dog(){
}
Dog.prototype.name=“旺財(cái)”;
Dog.prototype.eat=function(){
alert(this.name+“是個(gè)吃貨”);
}
var wangcai =new Dog();
wangcai.eat();
5、用混合方式來(lái)創(chuàng)建
function Car(name,price){
this.name=name;
this.price=price;
}
Car.prototype.sell=function(){
alert(“我是”+this.name+“,我現(xiàn)在賣(mài)”+this.price+“萬(wàn)元”);
}
var camry =new Car(“凱美瑞”,27);
camry.sell();
CSS優(yōu)先級(jí)算法如何計(jì)算? * 優(yōu)先級(jí)就近原則,同權(quán)重情況下樣式定義最近者為準(zhǔn);
載入樣式以最后載入的定位為準(zhǔn);
優(yōu)先級(jí)為:
!important 》 id 》 class 》 tag
important 比 內(nèi)聯(lián)優(yōu)先級(jí)高
iframe有那些缺點(diǎn)?
*iframe會(huì)阻塞主頁(yè)面的Onload事件;
*搜索引擎的檢索程序無(wú)法解讀這種頁(yè)面,不利于SEO;
*iframe和主頁(yè)面共享連接池,而瀏覽器對(duì)相同域的連接有限制,所以會(huì)影響頁(yè)面的并行加載。
使用iframe之前需要考慮這兩個(gè)缺點(diǎn)。如果需要使用iframe,最好是通過(guò)javascript
動(dòng)態(tài)給iframe添加src屬性值,這樣可以繞開(kāi)以上兩個(gè)問(wèn)題。
HTML5的離線儲(chǔ)存怎么使用,工作原理能不能解釋一下?
在用戶(hù)沒(méi)有與因特網(wǎng)連接時(shí),可以正常訪問(wèn)站點(diǎn)或應(yīng)用,在用戶(hù)與因特網(wǎng)連接時(shí),更新用戶(hù)機(jī)器上的緩存文件。
原理:HTML5的離線存儲(chǔ)是基于一個(gè)新建的.appcache文件的緩存機(jī)制(不是存儲(chǔ)技術(shù)),通過(guò)這個(gè)文件上的解析清單離線存儲(chǔ)資源,這些資源就會(huì)像cookie一樣被存儲(chǔ)了下來(lái)。之后當(dāng)網(wǎng)絡(luò)在處于離線狀態(tài)下時(shí),瀏覽器會(huì)通過(guò)被離線存儲(chǔ)的數(shù)據(jù)進(jìn)行頁(yè)面展示。
如何使用:
1、頁(yè)面頭部像下面一樣加入一個(gè)manifest的屬性;
2、在cache.manifest文件的編寫(xiě)離線存儲(chǔ)的資源;
CACHE MANIFEST
#v0.11
CACHE:
js/app.js
css/style.css
NETWORK:
resourse/logo.png
FALLBACK:
/ /offline.html
3、在離線狀態(tài)時(shí),操作window.applicationCache進(jìn)行需求實(shí)現(xiàn)。
Ajax是什么? 如何創(chuàng)建一個(gè)Ajax?
ajax的全稱(chēng):AsynchronousJavascript And XML。
異步傳輸+js+xml。
所謂異步,在這里簡(jiǎn)單地解釋就是:向服務(wù)器發(fā)送請(qǐng)求的時(shí)候,我們不必等待結(jié)果,而是可以同時(shí)做其他的事情,等到有了結(jié)果它自己會(huì)根據(jù)設(shè)定進(jìn)行后續(xù)操作,與此同時(shí),頁(yè)面是不會(huì)發(fā)生整頁(yè)刷新的,提高了用戶(hù)體驗(yàn)。
?。?)創(chuàng)建XMLHttpRequest對(duì)象,也就是創(chuàng)建一個(gè)異步調(diào)用對(duì)象
(2)創(chuàng)建一個(gè)新的HTTP請(qǐng)求,并指定該HTTP請(qǐng)求的方法、URL及驗(yàn)證信息
?。?)設(shè)置響應(yīng)HTTP請(qǐng)求狀態(tài)變化的函數(shù)
(4)發(fā)送HTTP請(qǐng)求
?。?)獲取異步調(diào)用返回的數(shù)據(jù)
?。?)使用JavaScript和DOM實(shí)現(xiàn)局部刷新
同步和異步的區(qū)別?
同步:瀏覽器訪問(wèn)服務(wù)器請(qǐng)求,用戶(hù)看得到頁(yè)面刷新,重新發(fā)請(qǐng)求,等請(qǐng)求完,頁(yè)面刷新,新內(nèi)容出現(xiàn),用戶(hù)看到新內(nèi)容,j進(jìn)行下一步操作。
異步:瀏覽器訪問(wèn)服務(wù)器請(qǐng)求,用戶(hù)正常操作,瀏覽器后端進(jìn)行請(qǐng)求。等請(qǐng)求完,頁(yè)面不刷新,新內(nèi)容也會(huì)出現(xiàn),用戶(hù)看到新內(nèi)容。
AMD(Modules/Asynchronous-Definition)、CMD(Common ModuleDefinition)規(guī)范區(qū)別?
AsynchronousModule Definition,異步模塊定義,所有的模塊將被異步加載,模塊加載不影響后面語(yǔ)句運(yùn)行。所有依賴(lài)某些模塊的語(yǔ)句均放置在回調(diào)函數(shù)中。
區(qū)別:
1. 對(duì)于依賴(lài)的模塊,AMD 是提前執(zhí)行,CMD 是延遲執(zhí)行。不過(guò)RequireJS 從 2.0 開(kāi)始,也改成可以延遲執(zhí)行(根據(jù)寫(xiě)法不同,處理方式不同)。CMD 推崇 as lazy as possible.
2. CMD 推崇依賴(lài)就近,AMD 推崇依賴(lài)前置??创a:
// CMD
define(function(require,exports, module) {
var a = require(‘。/a’)
a.doSomething()
// 此處略去 100 行
var b = require(‘。/b’) // 依賴(lài)可以就近書(shū)寫(xiě)
b.doSomething()
// 。。。
})
// AMD 默認(rèn)推薦
define([‘。/a’,‘。/b’], function(a, b) { // 依賴(lài)必須一開(kāi)始就寫(xiě)好
a.doSomething()
// 此處略去 100 行
b.doSomething()
// 。。。
})
DOM操作——怎樣添加、移除、移動(dòng)、復(fù)制、創(chuàng)建和查找節(jié)點(diǎn)?
?。?)創(chuàng)建新節(jié)點(diǎn)
createDocumentFragment() //創(chuàng)建一個(gè)DOM片段
createElement() //創(chuàng)建一個(gè)具體的元素
createTextNode() //創(chuàng)建一個(gè)文本節(jié)點(diǎn)
(2)添加、移除、替換、插入
appendChild()
removeChild()
replaceChild()
insertBefore() //在已有的子節(jié)點(diǎn)前插入一個(gè)新的子節(jié)點(diǎn)
?。?)查找
getElementsByTagName() //通過(guò)標(biāo)簽名稱(chēng)
getElementsByName() //通過(guò)元素的Name屬性的值(IE容錯(cuò)能力較強(qiáng),會(huì)得到一個(gè)數(shù)組,其中包括id等于name值的)
getElementById() //通過(guò)元素Id,唯一性
什么叫優(yōu)雅降級(jí)和漸進(jìn)增強(qiáng)?
優(yōu)雅降級(jí):Web站點(diǎn)在所有新式瀏覽器中都能正常工作,如果用戶(hù)使用的是老式瀏覽器,則代碼會(huì)針對(duì)舊版本的IE進(jìn)行降級(jí)處理了,使之在舊式瀏覽器上以某種形式降級(jí)體驗(yàn)卻不至于完全不能用。
如:border-shadow
漸進(jìn)增強(qiáng):從被所有瀏覽器支持的基本功能開(kāi)始,逐步地添加那些只有新版本瀏覽器才支持的功能,向頁(yè)面增加不影響基礎(chǔ)瀏覽器的額外樣式和功能的。當(dāng)瀏覽器支持時(shí),它們會(huì)自動(dòng)地呈現(xiàn)出來(lái)并發(fā)揮作用。
如:默認(rèn)使用flash上傳,但如果瀏覽器支持HTML5 的文件上傳功能,則使用HTML5實(shí)現(xiàn)更好的體驗(yàn);
jsonp的原理是動(dòng)態(tài)插入script標(biāo)簽
跨子域可以采用iframe proxy的方式,支持GET和POST,支持異步POST。缺點(diǎn)是:范圍較窄,限定在“跨子域”,而且需要在目標(biāo)服務(wù)器增加額外的文件。
JSONP的方式,支持雙向通信。只支持GET。缺點(diǎn)是:不支持POST,同時(shí)需要目標(biāo)服務(wù)器在服務(wù)端支持。
iframe的window.name的方式,支持跨主域。缺點(diǎn)是:不支持POST,不過(guò)已經(jīng)很贊了:)
HTML5postMessage,支持雙向通信。缺點(diǎn)是:僅限于HTML5。結(jié)合window.name的方式,就很贊了
iframe + location.hash的方式跨主域,功能強(qiáng)大,兼容性好,支持跨域的js調(diào)用,支持雙向通信。缺點(diǎn)是:太復(fù)雜,會(huì)嵌套太多的iframe,同時(shí)數(shù)據(jù)直接寫(xiě)在url中,數(shù)據(jù)大小受限而且數(shù)據(jù)暴露在外。
利用Flash跨域,優(yōu)點(diǎn)是支持強(qiáng)大,相對(duì)簡(jiǎn)單。缺點(diǎn)是:依賴(lài)flash,需要在服務(wù)器更目錄放置crossdomain.xml文件。
為什么要有同源限制?
我們舉例說(shuō)明:比如一個(gè)黑客程序,他利用Iframe把真正的銀行登錄頁(yè)面嵌到他的頁(yè)面上,當(dāng)你使用真實(shí)的用戶(hù)名,密碼登錄時(shí),他的頁(yè)面就可以通過(guò)Javascript讀取到你的表單中input中的內(nèi)容,這樣用戶(hù)名,密碼就輕松到手了。
作用域鏈的理解
每一段js代碼(全局代碼或函數(shù))都有一個(gè)與之關(guān)聯(lián)的作用域鏈(scope chain)。
當(dāng)js需要查找變量x值的時(shí)候(這個(gè)過(guò)程稱(chēng)為變量解析(variable resolution)),它會(huì)從鏈的第一個(gè)對(duì)象開(kāi)始查找,如果這個(gè)對(duì)象有一個(gè)名為x的屬性,則會(huì)直接使用這個(gè)屬性的值,如果第一個(gè)對(duì)象中沒(méi)有名為x的屬性,js會(huì)繼續(xù)查找鏈上的下一個(gè)對(duì)象。如果第二個(gè)對(duì)象依然沒(méi)有名為x的屬性,則會(huì)繼續(xù)查找下一個(gè),以此類(lèi)推。
如果作用域鏈上沒(méi)有任何一個(gè)對(duì)象含有屬性x,那么就認(rèn)為這段代碼的作用域鏈上不存在x,并最終拋出一個(gè)引用錯(cuò)誤(ReferenceError)異常。
評(píng)論
查看更多