構(gòu)造函數(shù)(new運(yùn)算符)
function Foo(name,age){
// this = {}; 默認(rèn)有這一行漩勤,創(chuàng)建一個(gè)空對(duì)象
this.name = name;
this.age = age;
this.class = 'class-1';
// return this; // 默認(rèn)有這一行焕妙,可寫可不寫(建議不寫)
}
var foo = new Foo('zhangsan',20);
// var foo2 = new Foo('lucy',22); // 可創(chuàng)建多個(gè)對(duì)象
一個(gè)新對(duì)象被創(chuàng)建盯腌。它繼承自Foo.prototype践剂。
分析:new Foo('zhangsan',20)
的時(shí)候鬼譬,創(chuàng)建了一個(gè)新對(duì)象(空對(duì)象),這個(gè)對(duì)象繼承了構(gòu)造函數(shù)Foo的原型對(duì)象(也就是Foo.prototype)構(gòu)造函數(shù)Foo被執(zhí)行逊脯。執(zhí)行的時(shí)候优质,相應(yīng)的參數(shù)會(huì)被傳入,同時(shí)上下文(this)會(huì)被指定為這個(gè)新實(shí)例军洼。在不傳遞任何參數(shù)的情況下巩螃,new foo相當(dāng)于new foo()。
分析:構(gòu)造函數(shù)Foo被執(zhí)行匕争,參數(shù)'zhangsan',20
被傳入避乏,this指向了新的實(shí)例foo。所以通過構(gòu)造函數(shù)中的this.name = name
甘桑,新實(shí)例foo的屬性name被設(shè)置為'zhangsan'
拍皮。如果構(gòu)造函數(shù)返回了一個(gè)“對(duì)象”歹叮,那么這個(gè)對(duì)象會(huì)取代整個(gè)new出來的結(jié)果。
如果構(gòu)造函數(shù)沒有返回對(duì)象春缕,那么new出來的結(jié)果為經(jīng)過步驟1和步驟2創(chuàng)建并執(zhí)行后的對(duì)象盗胀,這是我們最常用的情況艘蹋。
分析:返回步驟1和步驟2創(chuàng)建并執(zhí)行后的對(duì)象給實(shí)例
補(bǔ)充:在構(gòu)造函數(shù)里返回一個(gè)對(duì)象有什么意義嗎锄贼,應(yīng)用在哪里?
回答:比如做權(quán)限管理的時(shí)候女阀,符合條件的返回正常的實(shí)例宅荤,非法的可以傳過去另一個(gè)對(duì)象,但是對(duì)于用戶來說拿到的都是一個(gè)對(duì)象
注意:約定俗成浸策,構(gòu)造函數(shù)以大寫字母開頭
構(gòu)造函數(shù)(new運(yùn)算符) - 擴(kuò)展
-
var a = {};
其實(shí)是var a = new Object();
的語法糖
下面兩種方式是一樣的冯键。
var a = {name:'zhangsan'};
var b = new Object({name:'zhangsan'});
-
var a = [];
其實(shí)是var a = new Array();
的語法糖
下面兩種方式是一樣的。
var a = [1,2,3];
var b = new Array(1,2,3);
-
function Foo(){...}
其實(shí)是var Foo = new Function(){...}
- 使用instanceOf判斷一個(gè)函數(shù)是否是一個(gè)變量的的構(gòu)造函數(shù)
var a = {name:'zhangsan'};
var b = [1,2,3];
function Foo1(){
console.log('hello 1');
}
console.log(a instanceof Object); // true
console.log(b instanceof Array); // true
console.log(Foo1 instanceof Function); // true
創(chuàng)建對(duì)象有幾種方法
第一種方法:使用字面量庸汗,或者new Object創(chuàng)建對(duì)象
// 字面量方式創(chuàng)建對(duì)象
var object1 = {name:'object1'};
// 使用new Object創(chuàng)建對(duì)象惫确,以下兩種方式是一樣的
var object2 = new Object({name:'object2'});
var object3 = new Object();
object3.name = 'object3';
console.log(object1); // {name: "object1"}
console.log(object2); // {name: "object2"}
第二種方法:使用顯式的構(gòu)造函數(shù)創(chuàng)建對(duì)象
var M = function(name){
this.name = name
}
var object3 = new M('object3');
console.log(object3); // {name: "object3"}
第三種方法:使用Object.create創(chuàng)建對(duì)象
var P = {name:'object4'};
var object4 = Object.create(P);
console.log(object4); // {}
console.log(object4.name); // "name"
咦?這里object4的顯示結(jié)果怎么是空對(duì)象蚯舱?object4.name的顯示結(jié)果卻又是"name"了改化?
這里先留作懸念,在文章的最后解釋這個(gè)現(xiàn)象枉昏。
原型規(guī)則
- 所有的引用類型(數(shù)組陈肛,對(duì)象,函數(shù))兄裂,都具有對(duì)象特性句旱,即可自由擴(kuò)展屬性。
var obj = {}; obj.a = 100;
var arr = []; arr.a = 100;
function fn() {}; fn.a = 100;
console.log(obj.a); // 100
console.log(arr.a); // 100
console.log(fn.a); // 100
- 所有的引用類型(數(shù)組晰奖,對(duì)象谈撒,函數(shù)),都有一個(gè)
__proto__
屬性(原型對(duì)象/隱式原型)匾南,屬性值是一個(gè)普通對(duì)象港华。 - 所有的函數(shù),都有一個(gè)
prototype
屬性(原型對(duì)象/顯式原型)午衰,屬性值是一個(gè)普通對(duì)象立宜。 - 所有的引用類型(數(shù)組,對(duì)象臊岸,函數(shù))橙数,
__proto__
屬性,就是它的構(gòu)造函數(shù)的prototype
屬性帅戒。
console.log(obj.__proto__ === Object.prototype); // true
- 當(dāng)試圖得到一個(gè)對(duì)象的某個(gè)屬性時(shí)灯帮,如果這個(gè)對(duì)象本身沒有這個(gè)屬性崖技,那么會(huì)去它的原型對(duì)象中尋找。
// 構(gòu)造函數(shù)
function Foo(name){
this.name = name;
}
Foo.prototype.alertName = function (){
alert(this.name);
}
// 創(chuàng)建實(shí)例
var foo = new Foo('zhangsan');
foo.printName = function (){
console.log(this.name);
}
foo.printName(); // Console中顯示'zhangsan'
foo.alertName(); // 彈出框中顯示'zhangsan'
知識(shí)點(diǎn):實(shí)例的屬性钟哥,原型對(duì)象迎献,構(gòu)造函數(shù)中的this
指向的都是實(shí)例本身。
原型規(guī)則 - 擴(kuò)展
示例代碼:
var M = function(name){
this.name = name
}
var object3 = new M('object3');
1.構(gòu)造函數(shù)與實(shí)例的關(guān)系
通過new 構(gòu)造函數(shù)的方式腻贰,可以創(chuàng)建一個(gè)對(duì)象實(shí)例
在示例代碼中object3就是一個(gè)對(duì)象實(shí)例
2.構(gòu)造函數(shù)與原型對(duì)象的關(guān)系
每個(gè)函數(shù)都有prototype屬性吁恍,構(gòu)造函數(shù)也是函數(shù),所以也有prototype屬性播演。
在聲明構(gòu)造函數(shù)的時(shí)候(就是我們?cè)趯慺unction a(){...}的時(shí)候)冀瓦,JS引擎會(huì)給這個(gè)構(gòu)造函數(shù)自動(dòng)增加一個(gè)prototype屬性。這個(gè)prototype屬性會(huì)自動(dòng)初始化一個(gè)空對(duì)象写烤,也就是說prototype的值是一個(gè)對(duì)象(原型對(duì)象)翼闽。
console.log(M.prototype); // {}
prototype的英文解釋:原型,雛形
3.原型對(duì)象與構(gòu)造函數(shù)的關(guān)系
原型對(duì)象怎么區(qū)分自己是被哪個(gè)構(gòu)造函數(shù)所引用呢洲炊?
原型對(duì)象中會(huì)有一個(gè)構(gòu)造器感局,默認(rèn)是聲明的函數(shù)會(huì)引用自己。
console.log(M.prototype.constructor===M); // true
constructor的英文解釋:構(gòu)造器
4.實(shí)例與原型對(duì)象的關(guān)系
實(shí)例的proto屬性暂衡,和構(gòu)造函數(shù)的prototype屬性询微,完全是同一個(gè)東西。
console.log(object3.__proto__===M.prototype); // true
原型鏈
示例代碼:
var M = function(name){
this.name = name
}
M.prototype.say = function(){
console.log('say hi');
}
var object3 = new M('object3');
var object5 = new M('boject5');
console.log(object3.say()); // "say hi"
console.log(object5.say()); // "say hi"
可以看出來古徒,在構(gòu)造函數(shù)的原型對(duì)象上拓提,增加屬性和方法,那么這個(gè)構(gòu)造函數(shù)的實(shí)例都可以共用這些屬性和方法隧膘。
原型鏈的基本原理:
任何一個(gè)實(shí)例對(duì)象代态,可以通過原型鏈找到原型對(duì)象。而原型對(duì)象上所有的屬性和方法都是對(duì)實(shí)例共享的疹吃,這就是原型鏈的基本原理
JS引擎對(duì)JS對(duì)象的分析方式:
在訪問一個(gè)對(duì)象實(shí)例的屬性或方法時(shí)蹦疑,如果在實(shí)例本身沒有找到這個(gè)屬性或方法,會(huì)往上在實(shí)例的原型對(duì)象(實(shí)例.__proto__
)上查找這個(gè)屬性或方法萨驶。
如果還沒有找到這個(gè)屬性或方法歉摧,會(huì)再往上在原型對(duì)象的原型對(duì)象(原型對(duì)象.__proto__ ,也可以說是:實(shí)例.__proto__.__proto__
)上查找這個(gè)屬性或方法腔呜。
依次類推叁温,直到找到Object.prototype,如果還沒有找到核畴,那么就會(huì)提示這個(gè)屬性或方法沒有找到膝但。
知識(shí)點(diǎn)
- 只有函數(shù)有prototype屬性,對(duì)象是沒有的
- 函數(shù)也是一個(gè)對(duì)象谤草,所以函數(shù)也會(huì)有
__proto__
屬性
M.__proto__===Function.prototype
的結(jié)果是true(這里的M是以上示例代碼中的構(gòu)造函數(shù)M)
可以理解為:M的構(gòu)造函數(shù)是Function
也可以理解:M函數(shù)是Function函數(shù)的實(shí)例
instanceof的原理
instanceof用來判斷某個(gè)對(duì)象是否是某個(gè)構(gòu)造函數(shù)的實(shí)例跟束。
f instanceof Foo
的判斷邏輯是莺奸,f的__proto__
一層層往上,能否對(duì)應(yīng)到Foo.prototype
示例代碼:
var M = function(name){
this.name = name
}
var object3 = new M('object3');
console.log(object3 instanceof M); // true
console.log(object3 instanceof Object); // true
分析:
Object構(gòu)造了M冀宴,M構(gòu)造了object3灭贷,這構(gòu)成了一條原型鏈。
用instanceof來判斷的時(shí)候略贮,只要在這條原型鏈上甚疟,返回結(jié)果都是true。
這是不夠嚴(yán)謹(jǐn)?shù)呐偎啵驗(yàn)槲覀儫o法用instanceof來判斷object3的直接構(gòu)造函數(shù)是哪個(gè)古拴。
那該怎么知道object3的直接構(gòu)造函數(shù)是哪個(gè)呢箩帚?這是需要用到constructor了真友。
console.log(object3.__proto__.constructor===M); // true
console.log(object3.__proto__.constructor===Object); // false
關(guān)于使用Object.create創(chuàng)建對(duì)象的分析
回顧一下文章開頭提到的現(xiàn)象,以下方式創(chuàng)建的object4紧帕,直接顯示的話是一個(gè)空對(duì)象盔然,然而用object4.name取值是"object4"。
var P = {name:'object4'};
var object4 = Object.create(P);
console.log(object4); // {}
console.log(object4.name); // "name"
console.log(object4.__proto__===P); // true
分析:
首先是嗜,解釋下Object.create(P)的意思愈案。Object.create(P)會(huì)創(chuàng)建一個(gè)空對(duì)象,而這個(gè)空對(duì)象的原型對(duì)象鹅搪,就是參數(shù)P站绪。
所以,object4 = Object.create(P);
會(huì)把對(duì)象P作為新對(duì)象的原型對(duì)象丽柿,然后把這個(gè)新對(duì)象賦給對(duì)象object4恢准。
object4本身是不具備這個(gè)屬性的,而通過原型鏈?zhǔn)悄茉L問object3.name的甫题。
常見問題
- 如何判斷一個(gè)變量是否是數(shù)組馁筐?
var a = [1,2,3];
var b = 10;
console.log(a instanceof Array); // true
console.log(b instanceof Array); // false
- 寫一個(gè)原型鏈繼承的例子
<div id="box">before</div>
<script type="text/javascript">
// 構(gòu)造函數(shù)
function Elem(id){
this.elem = document.getElementById(id);
}
// 設(shè)置原型對(duì)象的屬性
Elem.prototype.html = function(val){
if(val){
this.elem.innerHTML = val;
return this;
}else{
return this.elem.innerHTML;
}
}
Elem.prototype.on = function(type,fn){
this.elem.addEventListener(type,fn);
return this;
}
// 實(shí)例化對(duì)象
let elem = new Elem('box');
// 操作
elem.html('after').on('click',function(){
alert('message:after');
})
</script>
操作結(jié)果:
頁面顯示"after",且點(diǎn)擊"after"會(huì)有彈出框坠非,彈出框中的內(nèi)容是"message:after"