1. 對象的創(chuàng)建
- 對象直接量
-- 舉例:
var empty = {};
var point = {x:0, y:0};
var point = {x: point.x, y: point.y}
var book = {
"main title": "Javascript",
'sub-title': "The Definitive Guide",
author:{
firstname: "David"亏吝,
surname:"Flanagan"
}
}
- 通過new創(chuàng)建對象
--舉例:(除了下面的這些內(nèi)置的構(gòu)造函數(shù)核偿,還可以通過自定義的構(gòu)造函數(shù)來初始化新對象)
var z = new Object();
var a = new Array(); // 效果同[]
var d = new Date();
var r = new RegExp("js");
new 的過程是什么脐彩,發(fā)生了什么彬碱?
分析:
1.創(chuàng)建一個空對象
2.將空對象的原型指向new 后面的構(gòu)造函數(shù)的原型
3.改變構(gòu)造函數(shù)的this指向新的對象
4.返回這個新的對象
function create(){
// 刪除arguments的第一項(xiàng)生年,con等于刪除的那項(xiàng)蠢终,也就是constructor
let con =[].prototype.shift.call(arguments);
// 創(chuàng)建一個空的對象讳癌,并且可以訪問到構(gòu)造函數(shù)的原型
let newObj=Object.create(con.prototype);
// 綁定this, con的this現(xiàn)在指向newObj
let res = con.apply(newObj, arguments);
// 優(yōu)先返回構(gòu)造函數(shù)返回的對象
return res instanceof Object ? res : newObj;
}
-
Object.create()
-- 首先了解一下原型的概念:每一個js對象(除null外)都和另外一個對象相關(guān)聯(lián)穿稳,這個另外一個對象就是我們說的原型,每一個對象都從原型繼承屬性- 所有通過對象直接量創(chuàng)建的對象都具有同一個原型對象晌坤,并可以通過js代
碼Object.prototype獲得對原型對象的引用逢艘。
- 所有通過對象直接量創(chuàng)建的對象都具有同一個原型對象晌坤,并可以通過js代
- 通過new和構(gòu)造函數(shù)調(diào)用創(chuàng)建的對象的原型就是構(gòu)造函數(shù)的prototype屬性
值,因此通過new Object()創(chuàng)建的對象也繼承自O(shè)bject.prototype骤菠。
- 通過new和構(gòu)造函數(shù)調(diào)用創(chuàng)建的對象的原型就是構(gòu)造函數(shù)的prototype屬性
- new Array()創(chuàng)建的對象的原型是Array.prototype它改。
- new Date()創(chuàng)建的對象的原型是Date.prototype。
-- Object.create(第一個參數(shù)(對象的原型)商乎,第二個參數(shù)央拖?(用以對對象的屬性進(jìn)行進(jìn)一步描述))
var o1 = Object.create({x:1, y:2}); // o1繼承了屬性x和y
// 通過傳入?yún)?shù)null,來創(chuàng)建一個沒有原型的對象
var o2 = Object.create(null); //o2不繼承任何屬性和方法
console.log(o2); // {} No properties
// 通過傳入?yún)?shù)Object.prototype可以創(chuàng)建一個普通的空對象
var o3 = Object.create(Object.prototype); // o3和{} 和new Object()一樣
console.log(o3); // {}
/**通過原型繼承創(chuàng)建一個新對象
1.inherit()返回了一個繼承自原型p的屬性的新對象
*/
function inherit(p) {
// 首先判斷p是否為null或者undefined,因?yàn)閜是一個對象
if (p == null || p == undefined) throw TypeError();
//如果Object.create()存在直接使用它創(chuàng)建對象
if (Object.create) {
return Object.create(p);
}
// 如果不存在,先判斷p是否是一個對象或者函數(shù)鹉戚,不是則報(bào)錯
var typeObject = typeof(p);
if (typeObject !== 'object' && typeObject !== 'function') throw TypeError();
function f(){}; // 定義一個空的構(gòu)造函數(shù)
f.prototype = p; // 將原型屬性設(shè)置為p
return new f(); // 使用f()創(chuàng)建p的繼承對象
}
2.繼承
- 通過【某種方式】讓一個對象可以訪問到另一個對象中的屬性和方法鲜戒,我們把這種方式稱之為繼承。
1)原型鏈繼承:
//實(shí)現(xiàn):重寫原型對象抹凳,使用一個新類型實(shí)例替換
//核心代碼:
//SuperType的所有屬性和方法都會在subType的原型中查詢到
subType.prototype = new SuperType();
//缺點(diǎn):多個實(shí)例對引用類型操作會被篡改
2)借用構(gòu)造函數(shù)實(shí)現(xiàn)繼承
//實(shí)現(xiàn):使用父類的構(gòu)造函數(shù)來增強(qiáng)子類實(shí)例,等同于復(fù)制父類的實(shí)例給子類(不使用原型)
//核心代碼:
//SuperType的所有屬性和方法都會被復(fù)制,并且在subType類中直接查詢到
SuperType.call(this)
創(chuàng)建子類實(shí)例時調(diào)用SuperType構(gòu)造函數(shù)泄伪,于是SubType的每個實(shí)例都會將SuperType的屬性復(fù)制一份
// 缺點(diǎn):
//只能繼承父類的實(shí)例屬性和方法,不能繼承原型屬性/方法
//無法實(shí)現(xiàn)復(fù)用柏蘑,每個子類都有父類實(shí)例函數(shù)的副本,影響性能
3)組合繼承
function SubType(){
SuperType.call(this);
}
SubType.prototype=new SuperType()粹庞;
console.log(SubType.prototype.constructor===SuperType);//true
//關(guān)于為什么要修復(fù)構(gòu)造函數(shù)的指向可以查閱資料:https://blog.csdn.net/Jane617_min/article/details/79744986
//如果不加下面這句代碼的話咳焚,subType這個類的constructor指向的就是SuperType
SubType.prototype.constructor = SubType;
//缺點(diǎn):
//在使用子類創(chuàng)建實(shí)例對象時,其原型中會存在兩份相同的屬性/方法信粮。
4)原型式繼承
var SubType = object.create(SuperType);
//直接進(jìn)行一個淺復(fù)制黔攒,將構(gòu)造函數(shù)SubType的原型直接指向SuperType
//缺點(diǎn):
//沒有辦法傳遞參數(shù)
//存在數(shù)據(jù)篡改的可能,因?yàn)檫@個屬于淺復(fù)制
5)寄生式繼承
//作用:為構(gòu)造函數(shù)新增屬性和方法强缘,以增強(qiáng)函數(shù)
//基于原型式繼承督惰,增強(qiáng)對象,返回構(gòu)造函數(shù)
function createAnother(SuperType){
var clone = object.create(SuperType);
clone.sayHi=function(){
alter("hi");
}
return clone;
}
//缺點(diǎn):
//(同原型式繼承)
//1.多個實(shí)例的引用類型屬性指向相同旅掂,存在篡改的可能赏胚;
//2.無法傳遞參數(shù)
6)寄生式組合繼承(最成熟也是現(xiàn)在庫實(shí)現(xiàn)的方法)
//首先借用構(gòu)造函數(shù)傳遞增強(qiáng)子類實(shí)例屬性(支持傳參和避免篡改)
function SubType(name, age){
SuperType.call(this, name);
this.age=age;
}
// 寄生模式
function inheritPrototype(subType, superType){
var prototype = Object.create(superType.prototype);//重寫原型失去了默認(rèn)的constructor
prototype.constructor=subType;//彌補(bǔ)上一步操作造成的constrictor缺失
subType.prototype = prototype;//將新創(chuàng)建的對象賦值給子類的原型
}
7)混入方式繼承多個對象
// 重點(diǎn):使用Object.assign(target, source):只會拷貝源對象自身的并且可枚舉的屬性到目標(biāo)對象
function MyClass(){
SuperClass.call(this);
OtherSuperClass.call(this);
}
MyClass.prototype = Object.create(SuperClass.prototype);
Object.assign(MyClass.prototype,OtherSuperClass.prototype);
MyClass.prototype.constructor=MyClass;
8)es6類繼承extends
// extends關(guān)鍵字主要用于類聲明或者類表達(dá)式中,以創(chuàng)建一個類商虐,該類是另一個類的子類
class Rectangle{
constructor(height, width){
this.height = height;
this.width = width;
}
}
//繼承
class Square extends Rectangle{
constructor(length){
//super在這里表示父類的構(gòu)造函數(shù)觉阅,用來新建一個父類的實(shí)例對象。
//子類構(gòu)造函數(shù)調(diào)用super()時秘车,會執(zhí)行一次父類構(gòu)造函數(shù)典勇。
//在子類的構(gòu)造函數(shù)中,只有調(diào)用super()之后叮趴,才可以使用this關(guān)鍵字割笙,否則會報(bào)錯
super(length, length);
}
}
es5和es6在繼承上的區(qū)別:
- es5是:先創(chuàng)造一個獨(dú)立的子類的實(shí)例對象,再將父類的方法和屬性添加到子類對象上眯亦,屬于“實(shí)例在前伤溉,繼承在后”;
- es6是:先將父類的屬性和方法加到一個空的對象上妻率,然后再將該類作為子類的實(shí)例對象乱顾,屬于“繼承在前,實(shí)例在后”