一.原型規則
1.所有的引用型別
(陣列
,物件
,函式
),都具有物件特性,即可自由擴充套件屬性,即xxx.a
,xxx.b
等(null
除外)
2.所有的引用型別
,都有__proto__
屬性(隱式原型),屬性值是一個普通的物件
3.所有的函式
,都有一個prototype
屬性(顯式原型),屬性值也是一個普通的物件
4.所有引用型別
的__proto__
屬性指向它的建構函式
的prototype
的屬性值
5.當試圖得到一個引用型別
的屬性
時,如果這個物件本身沒有這個屬性,那麼會去它的__proto__
(即它的建構函式的prototype
中)尋找
二.js中建立物件的方法
1.Object建構函式:
let o1 = new Object({name: 'sxt'});
複製程式碼
2.物件字面量
let o2 = {name: 'sxt'};
複製程式碼
上面兩種方式的原型都是Object,建構函式也是Object,即
console.log(o1.__proto__ === Object.prototype); //true
console.log(o1.constructor === Object); //true
console.log(o2.__proto__ === Object.prototype); //true
console.log(o2.constructor === Object); //true
複製程式碼
缺點:建立很多物件時,會有很多重複的程式碼
3.建構函式
let F = function (name) {
this.name = name;
};
let o3 = new F('sxt');
複製程式碼
這種方式建立的物件的原型是F,建構函式也是F,即
console.log(o3.__proto__ === F.prototype); //true
console.log(o3.constructor === F); //true
複製程式碼
4.Object.create建立物件
let P = {name: 'sxt'};
let o4 = Object.create(P);
複製程式碼
這個物件的原型是P,建構函式是Object,即
console.log(o4.__proto__ === P); //true
console.log(o4.constructor === Object); //true
複製程式碼
而Object.create的內部實現類似於如下
Object.prototype.create2 = function (proto) {
function F() {
}
F.prototype = proto;
return new F();
};
複製程式碼
5.工廠模式
function createPerson(name, age) {
let o = new Object();
o.name = name;
o.age = age;
o.sayName = function () {
return this.name;
};
return o;
}
let person1 = createPerson('sxt1', 1);
let person2 = createPerson('sxt2', 2);
console.log(person1.sayName()); //sxt1
console.log(person2.sayName()); //sxt2
console.log(person1.constructor === Object); //true
console.log(person2.constructor === Object); //true
複製程式碼
優點:可以批量建立物件
缺點:物件無法識別,不能知道物件的型別
6.建構函式模式
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
return this.name;
}
}
let person1 = new Person('sxt1', 1);
console.log(person1.__proto__ === Person.prototype); //true
console.log(person1.constructor === Person); //true
複製程式碼
缺點:每個方法都要在每個例項上建立,等同下面
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = new Function("return this.name")
}
複製程式碼
所以每個例項的方法都不是相同的,如下
console.log(person1.sayName === person2.sayName); //false
複製程式碼
補充:
- 建構函式的特點:
- 沒有顯式建立物件
- 直接將屬性和方法賦給了this物件
- 沒有return語句
- 函式名大寫
- 建構函式其實也可以有返回值,如果是五種基本資料型別(
Null
,Undefined
,String
,Boolean
,Number
)的話,返回值還是這個例項,但是如果是複雜資料型別的話(Object
),則返回這個複雜資料型別,同時適用於其他引用型別(陣列,函式等)
返回String型別時:
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
return this.name;
};
return '1';
}
let person = new Person('sxt', 1);
console.log(person); // Person {name: "sxt", sayName: ƒ}
複製程式碼
返回Object型別時:
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
return this.name;
};
return {};
}
let person = new Person('sxt', 1);
console.log(person); // {}
複製程式碼
- new操作符的原理:
- 建立一個空物件,繼承自建構函式的原型物件,即空物件.
__proto__
= 建構函式.prototype
- 將this和這個物件關聯,即this指向該物件
- 執行建構函式中的程式碼,即為這個物件新增屬性
- 如果建構函式中返回一個物件,將會取代前面那個空物件,否則結果為那個空物件 否則結果為那個空物件
- 建立一個空物件,繼承自建構函式的原型物件,即空物件.
let new2 = function (func) {
let o = Object.create(func.prototype);
let k = func.call(o);
if (typeof k === 'object') {
return k;
}else {
return o;
}
};
function Person() {
this.name = 'sxt';
this.sayName = function () {
return this.name;
}
}
let person = new2(Person);
console.log(person.__proto__ === Person.prototype); //true
console.log(person.constructor === Person); //true
複製程式碼
7.原型模式
function Person() {
}
Person.prototype.name = 'sxt';
Person.prototype.age = 11;
Person.prototype.sayName = function () {
return this.name;
};
let person1 = new Person();
let person2 = new Person();
console.log(person1.sayName()); //sxt
console.log(person1.sayName()); //sxt
console.log(person1.sayName === person2.sayName); //true
複製程式碼
缺點:
Person.prototype.nums = [1, 2, 3];
即在原型物件建立引用型別時,所有的例項會共享,即當person1修改了這個屬性的值後,person2的屬性值也會改變
補充:
-
函式都有一個prototype指向函式的原型物件
-
原型物件只會取得constructor屬性,其他方法都從Object繼承而來
-
isPrototypeOf
:用來判斷一個原型物件是不是某例項物件的原型
console.log(Person.prototype.isPrototypeOf(person1)); //true
複製程式碼
getPrototypeOf
:用來獲取一個例項物件的原型
console.log(Object.getPrototypeOf(person1) === Person.prototype); //true
複製程式碼
-
hasOwnProperty
可以檢測這個屬性存在例項中(true)還是存在原型物件中(false) -
可以用
person.name = 'sxt2';
來遮蔽原型中的值,但是不能修改原型中的值,可以用delete person1.name;
來刪除例項中的值,然後訪問到原型中的值 -
in
操作符,不管在原型中還是例項中都會返回true,如"name" in person1
-
for in
:返回例項
中,原型
中可列舉
的屬性,如果例項屬性toString覆寫了原型中的方法,那麼會返回例項中的toString,即使原型中的Enumerable是false
function Person() {
this.age = 1;
}
Person.prototype.name = 'sxt';
Person.prototype.sayName = function () {
return this.name;
};
let person1 = new Person();
for (let item in person1) {
console.log(item) //age name sayName
}
複製程式碼
function Person() {
this.age = 1;
this.toString = function () {
console.log('例項中的toString');
}
}
Person.prototype.name = 'sxt';
Person.prototype.sayName = function () {
return this.name;
};
let person1 = new Person();
person1.toString() //例項中的toString
for (let item in person1) {
console.log(item) //age toString name sayName
}
複製程式碼
不應該把for in
用來迭代陣列,因為索引順序不一定有序
Object.keys()
,可以列舉出所有物件中的可列舉的例項屬性
function Person() {
this.age = 1;
this.toString = function () {
console.log('例項中的toString');
}
}
Person.prototype.name = 'sxt';
Person.prototype.sayName = function () {
return this.name;
};
let person1 = new Person();
console.log(Object.keys(person1)); //["age", "toString"]
複製程式碼
Object.getOwnPropertyNames()
可以列舉出該物件的所有例項屬性
console.log(Object.getOwnPropertyNames(Person.prototype)) // ["constructor", "name", "sayName"]
複製程式碼
- 原型的動態性:就算建立例項在修改原型之前,原型上的修改也能立即反映在例項中,如下
function Person() {
this.age = 1;
this.toString = function () {
console.log('例項中的toString');
}
}
let person1 = new Person();
Person.prototype.name = 'sxt';
Person.prototype.sayName = function () {
return this.name;
};
console.log(person1.sayName()) //sxt
複製程式碼
但是如果重寫了整個原型物件,會找不到這個屬性,因為切斷了原型物件和建構函式之間的聯絡
function Person() {
this.age = 1;
this.toString = function () {
console.log('例項中的toString');
}
}
let person1 = new Person();
Person.prototype = {
name: 'sxt',
sayName: function () {
return this.name;
}
};
console.log(person1.sayName()) //sxt
複製程式碼
8.組合建構函式模式和原型模式:
function Person(name, age) {
this.name = name;
this.age = age;
this.nums = [1, 2, 3];
}
Person.prototype.sayName = function () {
return this.name;
};
let person1 = new Person('sxt1', 1);
let person2 = new Person('sxt2', 2);
console.log(person1.sayName === person2.sayName);
person1.nums.push(4); //true
console.log(person1.nums); //[1, 2, 3, 4]
console.log(person2.nums); //[1, 2, 3]
複製程式碼
優點:
1.解決了每次都要在例項上建立出一個新的方法的問題
2.引用型別建立在例項上,不會共享,某個例項修改了它的值不會影響到其他例項
github地址:github.com/shuxiaotai/…
gitbook地址:shuxiaotai.gitbooks.io/front_summa…