Javascript對(duì)象與繼承

標(biāo)識(shí)符指變量,函數(shù),屬性的名字,或者函數(shù)的參數(shù);
對(duì)象是引用類(lèi)型的值,是引用類(lèi)型的一個(gè)實(shí)例;引用類(lèi)型是一種數(shù)據(jù)結(jié)構(gòu);
所有引用類(lèi)型默認(rèn)都繼承自O(shè)bject類(lèi)型,所有對(duì)象都繼承自O(shè)bject(JS高程);

創(chuàng)建對(duì)象的方式

1.工廠模式

原理:在普通函數(shù)中創(chuàng)建一個(gè)對(duì)象,通過(guò)函數(shù)傳參至對(duì)象內(nèi),返回對(duì)象;調(diào)用函數(shù)即可返回一個(gè)對(duì)象;
缺點(diǎn):沒(méi)有對(duì)象識(shí)別,及沒(méi)有對(duì)象類(lèi)型;
例子:

//1.工廠模式
function createPerson(name,age,job){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function(){
        alert(this.name);
    };
    return o;
}
var person1 = createPerson("Nicholas",29,"Software Engineer");
var person2 = createPerson("Greg",27,"Doctor");

2.構(gòu)造函數(shù)模式

原理:
使用new操作符調(diào)用構(gòu)造函數(shù);
將構(gòu)造函數(shù)的作用域賦給新對(duì)象(this指向新對(duì)象);
構(gòu)造函數(shù)內(nèi)部通過(guò)參數(shù)和this為新對(duì)象添加屬性;
返回新對(duì)象;
缺點(diǎn):每個(gè)實(shí)例對(duì)象的方法不是同一個(gè)Function實(shí)例;若將方法定義為全局函數(shù)又無(wú)任何封裝性可言;
例子:

//2.構(gòu)造函數(shù)模式
function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    };
    var person1 = new Person("Nicholas",29,"Software Engineer");
    var person2 = new Person("Greg",27,"Doctor");
}

3.原型模式

原理:
定義構(gòu)造函數(shù)
通過(guò)函數(shù)的prototype指針修改原型對(duì)象中的屬性和方法
缺點(diǎn):所有屬性在原型對(duì)象中修改,導(dǎo)致所有屬性被所有實(shí)例共享;

//3.原型模式
function Person(){
}
Person.prototype = {
    name:"Nicholas",
    age:29,
    job:"Software Engineer",
    sayName:function(){
        alert(this.name);
    }
};
var person1 = new Person;
var person2 = new Person;
person1.sayName();//"Nicholas"
person2.sayName();//"Nicholas"
alert(person1.sayName == person2.sayName)//true

4.組合使用構(gòu)造函數(shù)模式和原型模式

原理:
實(shí)例屬性在構(gòu)造函數(shù)中定義
共享屬性和方法在原型中定義

//組合使用構(gòu)造函數(shù)模式和原型模式
function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelby","Court"];
}
Person.prototype = {
    constructor : Person,
    sayName : function(){
        alert(this.name);
    }
}
var person1 = new Person("Nicholas",29,"Software Engineer");
var person2 = new Person("Greg",27,"Doctor");
person1.friends.push("Van");
alert(person1.friends);//"Shelby,Count,Van"
alert(person2.friends);//"Shelby,Count"
alert(person1.friends === person2.friends);//false
alert(person1.sayname === person2.sayname);//true

5.動(dòng)態(tài)原型模式

原理:在構(gòu)造函數(shù)中初始化原型,且僅在必要的時(shí)候,即僅在構(gòu)造函數(shù)中不存在此方法的時(shí)候,往原型中添加,保持了同時(shí)使用構(gòu)造函數(shù)和原型的優(yōu)點(diǎn);
例子:

//5.動(dòng)態(tài)原型模式
function Person (name,age,job){
    //屬性
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() {
        alert('Matthew');
    }

    //方法
    if(typeof this.sayname != 'function') {
        //若上面的sayName不存在,則在原型中添加此方法
        Person.prototype.sayName = function() {
            alert(this.name);
        }
    }
}
var friend = new Person('Nicholas',29,'Software Engineer');
friend.sayName();//'Matthew'

6.寄生構(gòu)造函數(shù)模式

原理:
同工廠模式的函數(shù)相同;
調(diào)用時(shí)使用new通過(guò)構(gòu)造函數(shù)創(chuàng)建對(duì)象;
由于在構(gòu)造函數(shù)中返回了一個(gè)對(duì)象,因此構(gòu)造函數(shù)本身不需要再返回實(shí)例;即重寫(xiě)了調(diào)用構(gòu)造函數(shù)時(shí)返回的值;
因此,構(gòu)造函數(shù)返回的對(duì)象與構(gòu)造函數(shù)原型沒(méi)有關(guān)系;
優(yōu)點(diǎn):
給已有的對(duì)象添加方法并返回,為新對(duì)象創(chuàng)建構(gòu)造函數(shù),比如創(chuàng)建一個(gè)具有額外方法的數(shù)組;
缺點(diǎn):
返回的對(duì)象與構(gòu)造函數(shù)原型沒(méi)有任何關(guān)系,因?yàn)樵跇?gòu)造函數(shù)中返回了對(duì)象重寫(xiě)調(diào)用構(gòu)造函數(shù)時(shí)本應(yīng)該返回的值;
例子:

function SpecialArray(){
    //創(chuàng)建數(shù)組
    var values = new Array();
    //添加值
    values.push.apply(values,arguments);
    //添加方法
    values.toPipedString = function(){
        return this.join("|");
    };
    //返回?cái)?shù)組
    return values;
}
var colors = new SpecialArray("red","blue","green");
alert(colors.toPipedString());

7.穩(wěn)妥構(gòu)造函數(shù)模式

原理:
在函數(shù)中創(chuàng)建一個(gè)對(duì)象;
在函數(shù)中定義一個(gè)方法訪問(wèn)其中的屬性;
返回對(duì)象;
調(diào)用時(shí)不使用new,通過(guò)普通函數(shù)返回對(duì)象;
優(yōu)點(diǎn):除在函數(shù)中定義的方法外,沒(méi)有其他方法可以訪問(wèn)屬性,安全;
例子:

function Person(name,age,job){
    var o = new Object();
    o.sayName = function(){
        alert(name);
    }
    return o;
}
var friend = Person("Nicholas",29,"Software Engineer");
friend.sayName();//"Nicholas"

繼承

1.原型鏈

通過(guò)將子引用類(lèi)型的原型指向父引用類(lèi)型的實(shí)例來(lái)繼承;


原型鏈繼承.jpg
//1.原型鏈繼承
function SuperType() {
    this.property = true;
}

SuperType.prototype.getSuperValue = function() {
    return this.property;
}

function SubType() {
    this.subproperty = false;
}

SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function() {
    return this.subproperty;
}

var instance = new SubType();
console.log(instance.getSuperValue());//true

缺點(diǎn):
1.所有的子引用類(lèi)型的實(shí)例都公用父引用類(lèi)型中構(gòu)造函數(shù)所定義的屬性,因?yàn)樽右妙?lèi)型的原型就是父引用類(lèi)型的實(shí)例,在上面的例子中,這個(gè)屬性就是property;
2.在創(chuàng)建子類(lèi)型的實(shí)例時(shí),沒(méi)有辦法在不影響所有實(shí)例對(duì)象的情況下,給父類(lèi)型構(gòu)造函數(shù)傳遞參數(shù);

2.借用構(gòu)造函數(shù)

通過(guò)call在子類(lèi)型構(gòu)造函數(shù)中調(diào)用父類(lèi)型構(gòu)造函數(shù);

function SuperType(name) {
    this.name = name;
}

function SubType() {
    SuperType.call(this,'Nicholas');
    //實(shí)例屬性
    this.age = 29;
}

var instance = new SubType();
console.log(instance.name);//'Nicholas'
console.log(instance.age);//29

缺點(diǎn):
方法都在構(gòu)造函數(shù)中定義,無(wú)法函數(shù)復(fù)用,而在原型中子類(lèi)型又無(wú)權(quán)訪問(wèn);

3.組合繼承

即將原型鏈繼承和借用構(gòu)造函數(shù)組合到一起;
屬性使用借用構(gòu)造函數(shù),方法使用原型鏈;

//3.組合繼承
function SuperType(name) {
    this.name = name;
    this.colors = ['red','blue','green'];
}

SuperType.prototype.sayName = function() {
    console.log(this.name);
}

function SubType(name,age) {
    SuperType.call(this,name);
    this.age = age;
}

SubType.prototype = new SuperType();
SubType.prototype.constructor = SuperType;
SubType.prototype.sayAge = function() {
    console.log(this.age);
}

var instance1 = new SubType('Nicholas',29);
instance1.colors.push('black');
console.log(instance1.colors);//'red,blue,green,black'
instance1.sayName();//'Nicholas'
instance1.sayAge();//29

var instance2 = new SubType('Greg',27);
console.log(instance2.colors);//'red,blue,green'
instance2.sayName();//'Greg'
instance2.sayAge();//27

4.原型式繼承

原型式繼承和原型鏈繼承的主要區(qū)別就是,不使用構(gòu)造函數(shù)(其實(shí)是通過(guò)函數(shù)new),通過(guò)一個(gè)簡(jiǎn)單的函數(shù)object實(shí)現(xiàn)(ES5已有此方法Object.create());比較輕便;

//4.原型式繼承
function object(o) {
    function F(){};
    F.prototype = o;
    return new F();
}
var person = {
    name:'Nicholas',
    friends:['Shelby','Court','Van']
}

var anotherPerson = object(person);
anotherPerson.name = 'Greg';
anotherPerson.friends.push('Rob');

console.log(person.friends);//'Shelby,Court,Van,Rob'

我們修改實(shí)例的namefriends,在person里會(huì)修改friends但是name沒(méi)有任何影響,這是因?yàn)?code>friends是引用類(lèi)型的值,和原型鏈的繼承一樣,原型上引用類(lèi)型的值(方法、數(shù)組等)修改會(huì)在所有的實(shí)例中反映出來(lái),所以方法(引用類(lèi)型)能夠共用;
但是name是基本類(lèi)型的值,基本類(lèi)型值無(wú)法修改,每次都是重新創(chuàng)建變量(會(huì)在內(nèi)存中新分配位置),賦值的同時(shí)就相當(dāng)于在實(shí)例上創(chuàng)建屬性了。

var anotherPerson = object(person);
console.log(anotherPerson.name); // Nicholas
anotherPerson.name = 'Greg';
anotherPerson.friends.push('Rob');
console.log(person.name); // Nicholas
console.log(person.friends); //'Shelby,Court,Van,Rob'

缺點(diǎn):
所有通過(guò)此函數(shù)繼承的對(duì)象都共用原對(duì)象屬性;和原型鏈繼承相同;

5.寄生式繼承

在原型式繼承上進(jìn)行了增強(qiáng);除了擁有原對(duì)象上的方法和屬性,還可以自定義自己的方法和屬性;

//5.寄生式繼承
function createObj(o) {
    var clone = Object.create(o);
    clone.sayHi = function() {
        console.log('hi');
    }
    return clone;
}

var person = {
    name:'Nicholas',
    friends:['Shelby','Court','Van']
}

var anotherPerson = createObj(person);
anotherPerson.sayHi();//'hi'

缺點(diǎn):
方法不能復(fù)用,降低效率,和構(gòu)造函數(shù)類(lèi)似;

6.寄生組合式繼承

組合式繼承會(huì)在SubType.prototype = new SuperType();SuperType.call(this,name);時(shí)分別第一次和第二次調(diào)用SuperType()構(gòu)造函數(shù),會(huì)降低效率;因此,使用寄生組合式繼承:

function SuperType(name){
    this.name = name;
    this.colors = ['red','blue','green'];
}

SuperType.prototype.sayName = function() {
    console.log(this.name);
}

//用此方法代替構(gòu)造函數(shù)實(shí)例賦值的方法
function inheritPrototype(SubType,SuperType) {
    var prototype = Object(SuperType.prototype);
    prototype.constructor = SubType;
    SubType.prototype = prototype;
}

inheritPrototype(SubType,SubperType);

SubType.prototype.sayAge = function() {
    console.log(this.age);
}

總結(jié)

構(gòu)造對(duì)象的方法,普遍使用組合構(gòu)造函數(shù)和原型鏈模式或動(dòng)態(tài)原型模式,繼承,則普遍使用組合式繼承或寄生組合式繼承
--整理自《Javasctipt高級(jí)程序設(shè)計(jì)》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容