一砌梆、構(gòu)造函數(shù)
構(gòu)造函數(shù)的函數(shù)名以大寫字母為開頭课蔬。
JavaScript規(guī)定弛说,一個函數(shù)可以用new關(guān)鍵字來調(diào)用麸拄。那么此時將按順序發(fā)生四件事情:
(1)隱秘的創(chuàng)建一個新的空對象
(2)將這個函數(shù)里面的this綁定到剛才創(chuàng)建隱秘新對象上
(3)執(zhí)行函數(shù)體里面的語句
(4)返回這個新的對象
function People(){
this.name = "小明";
this.age = 18;
this.sex = "男";
console.log(11);//會打印因為第三步執(zhí)行了函數(shù)體內(nèi)的語句
}
var xiaoming = new People();//11
console.log(xiaoming);//People {name: "小明", age: 18, sex: "男"}
xiaoming.name;//小明
我們發(fā)現(xiàn)People不僅能執(zhí)行還能通過new返回一個對象派昧,我們就稱呼像People這樣的函數(shù)為構(gòu)造函數(shù),而xiaoming這樣返回出來的對象就稱呼為構(gòu)造函數(shù)的實例拢切,(這里就是People的實例)
1.1注意事項
1.1.1如果構(gòu)造函數(shù)內(nèi)沒有this將返回一個空對象。
function People(){
for(let i = 0; i < 3; i++) {
console.log(i);
}
}
var xiaoming = new People();//0,1,2
console.log(xiaoming);//{}
1.1.2 構(gòu)造函數(shù)構(gòu)造函數(shù)中秆吵,不允許出現(xiàn)return語句
但出現(xiàn)return語句的時候淮椰,如果return的是一個對象,那么通過new放回出來的對象就是return出來的對象纳寂,如果是簡單數(shù)據(jù)類型主穗,返回正常情況下該返回的實例。
//正常情況
function People(){
this.name = "小明";
this.age = 18;
this.sex = "男";
}
var xiaoming = new People();
console.log(xiaoming);//People {name: "小明", age: 18, sex: "男"}
//return 一個對象
function People(){
this.name = "小明";
this.age = 18;
this.sex = "男";
return {a: 1, b: 2}
}
var xiaoming = new People();
console.log(xiaoming);// {a: 1, b: 2}
//return 簡單數(shù)據(jù)類型
function People(){
this.name = "小明";
this.age = 18;
this.sex = "男";
return 1
}
var xiaoming = new People();
console.log(xiaoming);//People {name: "小明", age: 18, sex: "男"}
二原型
原型的特點:
1毙芜、原型也是對象忽媒,原型是函數(shù)對象的一個屬性
2、原型自帶constructor屬性腋粥, constructor指定構(gòu)造函數(shù)
3晦雨、構(gòu)造函數(shù)創(chuàng)建出的對象會繼承原型的屬性和方法
每個函數(shù)都有原型。
function People(){
this.name = "小明";
this.age = 18;
this.sex = "男";
}
People.prototype.sayHello = function() {
console.log('你好');
}
var xiaoming = new People();
console.log(typeof People.prototype);//Object
console.log(People.prototype.constructor);//People
xiaoming.sayHello();//你好
function test(){};
console.log(test.prototype)//{}
2.1實例對象的原型
當一個對象被new出來時隘冲,這個實例對象會自帶一個_proto_屬性,這個屬性指向生出這個實例的構(gòu)造函數(shù)的原型闹瞧。
function People(){
this.name = "小明";
this.age = 18;
this.sex = "男";
}
var xiaoming = new People();
console.log(xiaoming.__proto__ === People.prototype)//true
注意:__proto__前后各有兩個英文狀態(tài)下的下劃線
我們把構(gòu)造函數(shù)的原型叫做構(gòu)造函數(shù)實例的原型對象。
__proto__屬性展辞,是Chrome自己的屬性奥邮,別的瀏覽器不兼容,但是別的瀏覽器也有原型對象,只不過不能通過proto進行訪問而已洽腺。
三應(yīng)用
如果我們吧方法定義在構(gòu)造函數(shù)內(nèi)部時
function People(name,age){
this.name = name;
this.age = age;
this.sayHello = function() {
console.log('hello')
}
}
let xiaoming = new People('小明',14);
let xiaogang = new People('小剛', 14);
xiaoming .sayHello;//hello
xiaogang.sayHello;//hello
在new一個xiaoming的時候,構(gòu)造函數(shù)中的代碼順序執(zhí)行,綁定了name,age,sayHello,new一個xiaogang的時候也是如此,你有沒有發(fā)現(xiàn),不同的對象擁有相同他方法.因此可以將復(fù)用的方法放在原型對象上.
以避免消耗內(nèi)存脚粟。
function People(name,age){
this.name = name;
this.age = age;
}
People.prototype.sayHello = function() {
console.log('hello')
}
let xiaoming = new People('小明',14);
let xiaogang = new People('小剛', 14);
xiaoming .sayHello();//hello
xiaogang.sayHello();//hello
console.log(xiaoming.sayHello === xiaogang.sayHello);//true
四常見的創(chuàng)建對象的模式
4.1工廠模式
function test(name,age) {
let o = new Object();
o.name = name;
o.age = age;
o.sayHello = function() {
console.log('helllo')
};
return o
};
let test1 = test('first',1);
let test2 = test('second', 2);
??模式雖然解決了創(chuàng)建多個相似對象的問題,但沒有解決對象標識的問題蘸朋。
4.2構(gòu)造函數(shù)模式
function Test(name,age) {
this.name = name;
this.age = age;
this.sayHello = function() {
console.log('helllo')
};
};
let test1 = test('first',1);
let test2 = test('second', 2);
相對于工廠模式,構(gòu)造函數(shù)模式的優(yōu)點:
1核无、沒有顯式地創(chuàng)建對象
2、直接將屬性和方法賦給了 this 對象
3度液、沒有 return 語句
構(gòu)造函數(shù)模式的缺點: 就是每個?法都要在每個實例上重新創(chuàng)建?遍厕宗。
4.3.原型函數(shù)模式
function People(){}
People.prototype = {
constructor: People,
name : 'xiaoming',
age : 18,
sayHello () {
console.log('hello')
}
}
let xiaoming = new People();
let xiaogang = new People();
原型模式的好處是所有對象實例共享它的屬性和方法(即所謂的共有屬性),
缺點:就是省略了為構(gòu)造函數(shù)傳遞初始值參數(shù),導致所有的實例對象都是相同的屬性和方法堕担,其主要問題在于已慢,如果原型上存在引用類型的值的屬性時:
function People(){}
People.prototype = {
constructor: People,
name : 'xiaoming',
age : 18,
sayHello () {
console.log('hello')
},
friend : ['a']
}
let xiaoming = new People();
let xiaogang = new People();
xiaoming.friend.push('b');
console.log(xiaoming.friend);//['a', 'b']
console.log(xiaogang.friend);//['a', 'b']
4.4、混合模式(構(gòu)造函數(shù)模式+原型模式)
構(gòu)造函數(shù)模式用于定義實例屬性霹购,原型模式用于定義方法和共享的屬性
function People(name,age){
this.name = name;
this.age = age
}
People.prototype = {
constructor: People,
sayHello () {
console.log('hello')
}
}
let xiaoming = new People('小明',1);
let xiaogang = new People('小剛',2);
五原型鏈
只要是對象佑惠,一定有原型對象(除了Object.prototype),而一個實例通過__proto__屬性可以知道它的原型對象齐疙,也就是它的構(gòu)造函數(shù)的原型膜楷。而它的原型對象也是對象,那么它的原型對象應(yīng)該也有原型對象贞奋。
function People() {};
let people = new People;
console.log(people.__proto__ );
console.log(people.__proto__ .__proto__.constructor);//Object
console.log(people.__proto__ .__proto__.__proto__);//null
Object是一個函數(shù)赌厅,是系統(tǒng)內(nèi)置的構(gòu)造函數(shù),用于創(chuàng)造對象的轿塔。Object.prototype是所有對象的原型鏈終點特愿。
所以,當我們在一個對象上打點調(diào)用某個方法的時候勾缭,系統(tǒng)會沿著原型鏈去尋找它的定義揍障,一直找到Object.prototype。
function People() {};
let people = new People;
Object.prototype.sayHello = function() {
console.log('hello')
};
people.sayHello();
Object.prototype是所有對象的原型鏈的終點俩由,所以我們直接給Object.prototype增加一個方法毒嫡,那么所有的對象都能調(diào)用這個方法.
5.1引用類型的構(gòu)造函數(shù)
我們可以利用原型鏈的機制,給數(shù)組對象增加方法.
所有的引用類型值幻梯,都有內(nèi)置構(gòu)造函數(shù)兜畸。比如
new Object()
new Array()
new Function()
new RegExp()
new Date();
以數(shù)組為例子礼旅;
let arr = [1,2,3,45,6];
console.log(arr.__proto__ === Array.prototype)//true
console.log(arr.__proto__.__proto__.constructor === Object)//true
let arr = [1,2,3,45,6];
let max = -Infinity;
arr.__proto__.max = function() {
for(let i = 0; i < arr.length; i++) {
if(arr[i] >= max) {
max = arr[i]
}
}
return max
}
arr.max()//45
5.2基本數(shù)據(jù)類型的包裝類
基本類型值膳叨,也有包裝類型。所謂包裝類型痘系,就是它的構(gòu)造函數(shù)菲嘴。
1、new Number()
2、new String()
3龄坪、new Boolean()
let num = 10;
console.log(num.__proto__ === Number.prototype)//true
console.log(num.__proto__.__proto__.constructor === Object)//true
let str= '10';
console.log(str.__proto__ === String.prototype)//true
console.log(str.__proto__.__proto__.constructor === Object)//true
let bol= true;
console.log(bol.__proto__ === Boolean.prototype)//true
console.log(bol.__proto__.__proto__.constructor === Object)//true
let un= undefined;
console.log(un.__proto__.constructor)//報錯
let nu= null;
console.log(nu.__proto__.constructor)//報錯
六對象與屬性
6.1打點調(diào)用
var obj = {
a : 1,
b : 2,
c : 3
}
obj.__proto__ = {
d : 4
}
console.log(obj.a); //1
console.log(obj.b); //2
console.log(obj.c); //3
console.log(obj.d); //4
6.2in
var obj = {
a : 1,
b : 2,
c : false
}
obj.__proto__ = {
d: 20
}
console.log("a" in obj); //true
console.log("b" in obj); //true
console.log("c" in obj); //true
console.log("d" in obj); //true
in和打點調(diào)用昭雌,如果自己或者只要在原型鏈上有這個方法就會被查找到并返回true。
通過for...in循環(huán)調(diào)用可枚舉的屬性健田。
var obj = {
a : 1,
b : 2,
c : false
}
obj.__proto__ = {
d: 20
}
for( let k in obj) {
console.log(k)//a,b,c,d.
}
6.2 hasOwnProperty
這個方法定義在了Object.prototype對象上面烛卧,所以任何一個Object都能夠擁有這個方法。
var obj = {
a : 1,
b : 2,
c : 3
}
obj.__proto__ = {
d : 4
}
console.log(obj.hasOwnProperty("a")); //true
console.log(obj.hasOwnProperty("b")); //true
console.log(obj.hasOwnProperty("c")); //true
console.log(obj.hasOwnProperty("d")); //false,在原型鏈上不在實例上
把自己身上的屬性輸出:
for(let k in obj){
obj.hasOwnProperty(k) && console.log(k);
}
把不在自己身上的屬性輸出:
var obj = {
a : 1,
b : 2,
c : 3
}
obj.__proto__ = {
d : 4
}
for(let k in obj){
if(!obj.hasOwnProperty(k) && k in obj) {
console.log(k)//d
}
}
6.3 定義多個屬性O(shè)bject.defineProperties()
var test = {};
Object.defineProperties(test, {
_name: {
value: 'chen'
},
name: {
get: function() {
return this._name;
},
set: function(newvalue) {
if (typeof newvalue != 'string') {
throw new Error(TypeError);
} else {
this._name = newvalue;
}
}
}
})
console.log(test.name);//chen
test.name = 10;//typeerror;
test.name = 'aa';
console.log(test.name);//aa
讀取屬性的描述對象Object.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptor(test, 'name')
//{enumerable: false, configurable: false, get: ?, set: ?}
6.4 instanceof
function Boy(){};
function Girl(){};
Girl.prototype = new Boy();
let xiaohong = new Girl();
console.log(xiaohong.constructor)//Boy;
console.log(xiaohong instanceof(Boy))//true
console.log(xiaohong instanceof(Girl))//true
首先妓局,實例會繼承原型對象的屬性和方法(當然包括constructor屬性)总放;這里Girl.prototype上的constructor本來應(yīng)該指向Girl,但由于
Girl.prototype = new Boy()好爬,new Boy()實例繼承了Boy.prototype上的constructor:Boy,最后new Girl()也繼承了它原型對象new Boy()的constructor局雄。所以xiaohong.constructor為Boy。
instanceof 運算符的機理: 遍訪xiaohong這個對象的原型鏈上的每個原型對象存炮,如果遍訪到這個原型對象炬搭,是某個構(gòu)造函數(shù)的prototype,那么就認為xiaohong是這個構(gòu)造函數(shù)的實例穆桂,返回true宫盔。
6.5檢測數(shù)據(jù)類型
Object.prototype.toString.call(123); //"[object Number]"
Object.prototype.toString.call("123"); //"[object String]"
Object.prototype.toString.call(true); //"[object Boolean]"
Object.prototype.toString.call(undefined); //"[object Undefined]"
Object.prototype.toString.call(null); //"[object Null]"
Object.prototype.toString.call(function(){}); //"[object Function]"
Object.prototype.toString.call([]); //"[object Array]"
Object.prototype.toString.call({}); //"[object Object]"
七 繼承
7.1原型鏈繼承
簡單來說就是把父類的實例作為子類的原型。
function People(name){
this.name = name;
}
People.prototype.sayHello = function(){
alert("你好我是" + this.name);
}
function Student(name,xuehao){
this.name = name;
this.xuehao = xuehao;
}
Student.prototype = new People('大明');
var xiaohong = new Student("小紅",1001);
這樣子類既可以繼承父類的屬性享完、方法又可以在自己追加方法灼芭。
不過當父類原型上具有引用類型值的屬性時,會有大問題:
function People(name){
this.name = name;
}
People.prototype.arr= [1,2,3]
function Student(name,xuehao){
this.name = name;
this.xuehao = xuehao;
}
Student.prototype = new People('大明');
var xiaohong = new Student("小紅",1001);
//**注意這里**
xiaohong.arr.push(4);
console.log(xiaohong.arr);//[1,2,3,4]
var xiaoming = new Student("小明",1001);
console.log(xiaoming.arr);//[1,2,3,4]
7.2構(gòu)造函數(shù)繼承
function People(name){
this.name = name;
}
People.prototype.sayHello = function(){
alert("你好我是" + this.name);
}
function Student(name,xuehao){
//核心
People.call(this,name);
this.xuehao = xuehao;
}
Student.prototype.study = function(){
alert("好好學習般又,天天向上");
}
var xiaohong = new Student("小紅",1001);
之前說過姿鸿,用call先執(zhí)行函數(shù),然后在執(zhí)行函數(shù)的時候改變this指向倒源,
那么 People.call(this,name),也就好理解了。
缺點句狼,所有的屬性和方法只能定義在構(gòu)造函數(shù)身上笋熬,無法實現(xiàn)函數(shù)的復(fù)用,子類也不能訪問到父類的原型腻菇。
7.3. 組合繼承
就是將原型鏈繼承和構(gòu)造函數(shù)繼承組合在一起;繼承兩個優(yōu)點胳螟。
function People(name){
this.name = name;
}
function Student(name,xuehao){
//構(gòu)造繼承
People.call(this,name);
this.xuehao = xuehao;
}
//核心語句,
//原型繼承
Student.prototype = new People('大明');
Student.prototype.study = function(){
alert("好好學習筹吐,天天向上");
}
var xiaohong = new Student("小紅",1001);
xiaohong.sayHello();
缺點:
調(diào)用了兩次父類構(gòu)造函數(shù)糖耸,一次是在創(chuàng)建子類原型時,一次是在子類構(gòu)造函數(shù)中丘薛。
7.4. 寄生組合繼承
function People(name){
this.name = name;
}
function Student(name,xuehao){
People.call(this,name);
this.xuehao = xuehao;
}
function Fn() {};
Fn.prototype = People.prototype
Student.prototype = new Fn();//核心語句嘉竟,
Student.prototype.study = function(){
alert("好好學習,天天向上");
}
var xiaohong = new Student("小紅",1001);
xiaohong.sayHello();
這里只調(diào)用了一次People構(gòu)造函數(shù),避免了組合繼承的缺點舍扰。
7.5. 圣杯模式
圣杯模式就是將借用的構(gòu)造函數(shù)封裝一下
function inherit(People,Student){
function Fn(){}
Fn.prototype = People.prototype
Student.prototype = new Fn();
Student.prototype.constructor = Student;//增強對象
}
inherit(People,Student )