前言
此文章為加深對(duì)JS中重要概念進(jìn)行理解路克,不建議沒有任何JS基礎(chǔ)的人看,只為加深對(duì)概念理解通過實(shí)際的例子养交,而不是看書以為自己讀懂了精算,可能幾天后就忘了,主要是為了理解核心概念碎连,以及對(duì)重難點(diǎn)解釋灰羽。
一切都是對(duì)象
“一切都是對(duì)象”這句話的重點(diǎn)在于如何去理解“對(duì)象”這個(gè)概念。
概念
JavaScript 中鱼辙,萬(wàn)物皆對(duì)象廉嚼!但對(duì)象也是有區(qū)別的。分為普通對(duì)象和函數(shù)對(duì)象座每,Object 前鹅、Function 是 JS 自帶的函數(shù)對(duì)象。
當(dāng)然峭梳,也不是所有的都是對(duì)象舰绘,值類型就不是對(duì)象。
function show(x) {
console.log(typeof x); // undefined
console.log(typeof 10); // number
console.log(typeof 'abc'); // string
console.log(typeof true); // boolean
console.log(typeof function () {}); //function
console.log(typeof [1, 'a', true]); //object
console.log(typeof { a: 10, b: 20 }); //object
console.log(typeof null); //object
console.log(typeof new Number(10)); //object
}
show();
以上代碼列出了typeof輸出的集中類型標(biāo)識(shí)葱椭,其中上面的四種(undefined, number, string, boolean)屬于簡(jiǎn)單的值類型捂寿,不是對(duì)象。剩下的幾種情況——函數(shù)孵运、數(shù)組秦陋、對(duì)象、null治笨、new Number(10)都是對(duì)象驳概。他們都是引用類型赤嚼。
對(duì)象——若干屬性的集合
概念
數(shù)組是對(duì)象,函數(shù)是對(duì)象顺又,對(duì)象還是對(duì)象更卒。
對(duì)象里面的一切都是屬性,只有屬性稚照,沒有方法
那么這樣方法如何表示呢蹂空?——方法也是一種屬性。因?yàn)樗膶傩员硎緸殒I值對(duì)的形式果录。
而且上枕,javascript中的對(duì)象可以任意的擴(kuò)展屬性,沒有class的約束弱恒。這個(gè)大家應(yīng)該都知道辨萍,就不再?gòu)?qiáng)調(diào)了。
先說(shuō)個(gè)最常見的例子:
var obj = {
a: 10,
b: function(x) {
alert(this.a + x)
},
c: {
name: "yzh",
age: 21
}
}
以上代碼中返弹,obj是一個(gè)自定義的對(duì)象分瘦,其中a、b琉苇、c就是它的屬性嘲玫,而且在c的屬性值還是一個(gè)對(duì)象,它又有name并扇、year兩個(gè)屬性去团。
這個(gè)可能比較好理解,那么函數(shù)和數(shù)組也可以這樣定義屬性嗎穷蛹?——當(dāng)然不行土陪,但是它可以用另一種形式,總之函數(shù)/數(shù)組之流肴熏,只要是對(duì)象鬼雀,它就是屬性的集合。
var fn = function () {
alert(100);
};
fn.a = 10;
fn.b = function () {
alert(123);
};
fn.c = {
name: "yzh",
age: 21
};
上段代碼中蛙吏,函數(shù)就作為對(duì)象被賦值了a源哩、b、c三個(gè)屬性——很明顯鸦做,這就是屬性的集合励烦。
(引用類型)都是對(duì)象,對(duì)象是屬性的集合泼诱。最需要了解的就是對(duì)象的概念坛掠。
創(chuàng)建對(duì)象
前言
這塊在《JS高級(jí)程序設(shè)計(jì)》也算是大章節(jié)下的一塊大內(nèi)容,我只把一些重要的概念寫出來(lái)讓大家理解,具體的深入要自己去看書中的講解屉栓。
函數(shù)和對(duì)象的關(guān)系
對(duì)象都是通過函數(shù)創(chuàng)建的
function Fn() {
this.name = 'yzh';
this.year = 1996;
}
var fn1 = new Fn();
有人可能會(huì)舉出如下反例
var obj = { a: 10, b: 20 };
var arr = [5, 'x', true];
這種做法屬于使用“快捷方式”舷蒲,在編程語(yǔ)言中,一般叫做“語(yǔ)法糖”友多。
其實(shí)以上代碼的本質(zhì)是:
//var obj = { a: 10, b: 20 };
//var arr = [5, 'x', true];
var obj = new Object();
obj.a = 10;
obj.b = 20;
var arr = new Array();
arr[0] = 5;
arr[1] = 'x';
arr[2] = true;
而其中的 Object 和 Array 都是函數(shù):
console.log(typeof (Object)); // function
console.log(typeof (Array)); // function
總結(jié):對(duì)象都是通過函數(shù)來(lái)創(chuàng)建的
prototype
函數(shù)也是一種對(duì)象阿纤。他也是屬性的集合,你也可以對(duì)函數(shù)進(jìn)行自定義屬性
每創(chuàng)建一個(gè)函數(shù)夷陋,就會(huì)同時(shí)創(chuàng)建函數(shù)的prototype對(duì)象。
這個(gè)prototype的屬性值是一個(gè)對(duì)象(屬性的集合胰锌,再次強(qiáng)調(diào)F啤),默認(rèn)的只有一個(gè)叫做constructor的屬性资昧,指向這個(gè)函數(shù)本身酬土。
function Fn() { }
Fn.prototype.name = '王福朋';
Fn.prototype.getYear = function () {
return 1988;
};
var fn = new Fn();
console.log(fn.name);
console.log(fn.getYear());
Fn是一個(gè)函數(shù),fn對(duì)象是從Fn函數(shù)new出來(lái)的格带,這樣fn對(duì)象就可以調(diào)用Fn.prototype中的屬性撤缴。
因?yàn)槊總€(gè)對(duì)象都有一個(gè)隱藏的屬性——“proto”,這個(gè)屬性引用了創(chuàng)建這個(gè)對(duì)象的函數(shù)的prototype叽唱。
即:fn.proto === Fn.prototype
這里的"proto"成為“隱式原型”
隱式原型
每個(gè)函數(shù)function都有一個(gè)prototype屈呕,即原型。這里再加一句話——每個(gè)對(duì)象都有一個(gè)proto棺亭,可成為隱式原型虎眨。proto用于指向創(chuàng)建它的構(gòu)造函數(shù)的原型對(duì)象
對(duì)象 person1 有一個(gè) proto屬性,創(chuàng)建它的構(gòu)造函數(shù)是 Person镶摘,構(gòu)造函數(shù)的原型對(duì)象是 Person.prototype 嗽桩,所以:
person1.proto == Person.prototype
又比如:obj這個(gè)對(duì)象本質(zhì)上是被Object函數(shù)創(chuàng)建的,因此obj.proto=== Object.prototype
在說(shuō)明“Object.prototype”之前凄敢,先說(shuō)一下自定義函數(shù)的prototype碌冶。自定義函數(shù)的prototype本質(zhì)上就是和 var obj = {} 是一樣的,都是被Object創(chuàng)建涝缝,所以它的proto指向的就是Object.prototype扑庞。
但是Object.prototype確是一個(gè)特例——它的proto指向的是null.
至于為什么簡(jiǎn)單解釋下:
所有的構(gòu)造器都來(lái)自于 Function.prototype,甚至包括根構(gòu)造器Object及Function自身拒逮。所有構(gòu)造器都繼承了·Function.prototype·的屬性及方法嫩挤。如length、call消恍、apply岂昭、bind
console.log(typeof Function.prototype) // function
console.log(typeof Object.prototype) // object
console.log(typeof Number.prototype) // object
console.log(typeof Boolean.prototype) // object
console.log(typeof String.prototype) // object
console.log(typeof Array.prototype) // object
console.log(typeof RegExp.prototype) // object
console.log(typeof Error.prototype) // object
console.log(typeof Date.prototype) // object
console.log(typeof Object.prototype) // object
知道了所有構(gòu)造器(含內(nèi)置及自定義)的proto都是Function.prototype,
Function.prototype的proto是誰(shuí)呢?
console.log(Function.prototype.proto === Object.prototype) // true
這說(shuō)明所有的構(gòu)造器也都是一個(gè)普通 JS 對(duì)象约啊,可以給構(gòu)造器添加/刪除屬性等邑遏。同時(shí)它也繼承了Object.prototype上的所有方法:toString、valueOf恰矩、hasOwnProperty等记盒。
最后Object.prototype的proto是誰(shuí)?
Object.prototype.proto === null // true
已經(jīng)到頂了外傅,為null纪吮。
原型鏈
概念
訪問一個(gè)對(duì)象的屬性時(shí),先在基本屬性中查找萎胰,如果沒有碾盟,再沿著proto這條鏈向上找
javascript中的繼承是通過原型鏈來(lái)體現(xiàn)的.
傳統(tǒng)原型語(yǔ)法
function Foo() {}
Foo.prototype.a = 100;
Foo.prototype.b = 200;
var f1 = new Foo();
f1.a = 10;
alert(f1.a); //10
alert(f1.b); //200
function Foo() {}
var f1 = new Foo();
f1.a = 10;
Foo.prototype.a = 100;
Foo.prototype.b = 200;
alert(f1.a); //10
alert(f1.b); //200
對(duì)象字面量方法添加屬性和方法的注意事項(xiàng)
function Foo() {}
Foo.prototype = {
a: 100,
b: 200
}
var f1 = new Foo();
f1.a = 10;
alert(f1.a); //10
alert(f1.b); //200
function Foo() {}
var f1 = new Foo();
f1.a = 10;
Foo.prototype = {
a: 100,
b: 200
}
alert(f1.a); //10
alert(f1.b); //undefined
原型的屬性和方法賦值要在,新建實(shí)例對(duì)象之前技竟,不然無(wú)法獲得原型的值和屬性冰肴,alert返回相應(yīng)的undefined
重寫原型對(duì)象問題
接上面的例子講,如果在實(shí)例上添加新屬性,這個(gè)屬性就會(huì)屏蔽原型對(duì)象中保存的同名屬性榔组,就是阻止訪問了屬性熙尉,而不是修改原型的屬性。
function Foo() {}
var f1 = new Foo();
f1.a = 10;
Foo.prototype = {
a: 100,
b: 200
}
alert(f1.a); //10
alert(f1.b); //undefined
總結(jié):重寫原型對(duì)象切斷了現(xiàn)有原型與任何之前已經(jīng)存在的對(duì)象實(shí)例之間的關(guān)系搓扯,它們的引用的仍然是最初的原型检痰。
End
暫時(shí)總結(jié)到此,有些知識(shí)點(diǎn)沒有講到锨推,可能需要大家自己去看書或查閱資料來(lái)理解攀细,本人理解也有限,文中若有難以理解的還望大神換個(gè)方式來(lái)闡述爱态。
未完待續(xù)
后續(xù)還有兩篇講解《執(zhí)行上下文與作用域》和《閉包》谭贪,最后一篇閉包可能會(huì)有一些前端面試題來(lái)講,并在文章末做個(gè)總結(jié)锦担。