Javascript对象与继承

标识符指变量,函数,属性的名字,或者函数的参数;
对象是引用类型的值,是引用类型的一个实例;引用类型是一种数据结构;
所有引用类型默认都继承自Object类型,所有对象都继承自Object(JS高程);

创建对象的方式

1.工厂模式

原理:在普通函数中创建一个对象,通过函数传参至对象内,返回对象;调用函数即可返回一个对象;
缺点:没有对象识别,及没有对象类型;
例子:

//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.构造函数模式

原理:
使用new操作符调用构造函数;
将构造函数的作用域赋给新对象(this指向新对象);
构造函数内部通过参数和this为新对象添加属性;
返回新对象;
缺点:每个实例对象的方法不是同一个Function实例;若将方法定义为全局函数又无任何封装性可言;
例子:

//2.构造函数模式
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.原型模式

原理:
定义构造函数
通过函数的prototype指针修改原型对象中的属性和方法
缺点:所有属性在原型对象中修改,导致所有属性被所有实例共享;

//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.组合使用构造函数模式和原型模式

原理:
实例属性在构造函数中定义
共享属性和方法在原型中定义

//组合使用构造函数模式和原型模式
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.动态原型模式

原理:在构造函数中初始化原型,且仅在必要的时候,即仅在构造函数中不存在此方法的时候,往原型中添加,保持了同时使用构造函数和原型的优点;
例子:

//5.动态原型模式
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.寄生构造函数模式

原理:
同工厂模式的函数相同;
调用时使用new通过构造函数创建对象;
由于在构造函数中返回了一个对象,因此构造函数本身不需要再返回实例;即重写了调用构造函数时返回的值;
因此,构造函数返回的对象与构造函数原型没有关系;
优点:
给已有的对象添加方法并返回,为新对象创建构造函数,比如创建一个具有额外方法的数组;
缺点:
返回的对象与构造函数原型没有任何关系,因为在构造函数中返回了对象重写调用构造函数时本应该返回的值;
例子:

function SpecialArray(){
    //创建数组
    var values = new Array();
    //添加值
    values.push.apply(values,arguments);
    //添加方法
    values.toPipedString = function(){
        return this.join("|");
    };
    //返回数组
    return values;
}
var colors = new SpecialArray("red","blue","green");
alert(colors.toPipedString());

7.稳妥构造函数模式

原理:
在函数中创建一个对象;
在函数中定义一个方法访问其中的属性;
返回对象;
调用时不使用new,通过普通函数返回对象;
优点:除在函数中定义的方法外,没有其他方法可以访问属性,安全;
例子:

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.原型链

通过将子引用类型的原型指向父引用类型的实例来继承;


原型链继承.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

缺点:
1.所有的子引用类型的实例都公用父引用类型中构造函数所定义的属性,因为子引用类型的原型就是父引用类型的实例,在上面的例子中,这个属性就是property
2.在创建子类型的实例时,没有办法在不影响所有实例对象的情况下,给父类型构造函数传递参数;

2.借用构造函数

通过call在子类型构造函数中调用父类型构造函数;

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

function SubType() {
    SuperType.call(this,'Nicholas');
    //实例属性
    this.age = 29;
}

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

缺点:
方法都在构造函数中定义,无法函数复用,而在原型中子类型又无权访问;

3.组合继承

即将原型链继承和借用构造函数组合到一起;
属性使用借用构造函数,方法使用原型链;

//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.原型式继承

原型式继承和原型链继承的主要区别就是,不使用构造函数(其实是通过函数new),通过一个简单的函数object实现(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'

我们修改实例的namefriends,在person里会修改friends但是name没有任何影响,这是因为friends是引用类型的值,和原型链的继承一样,原型上引用类型的值(方法、数组等)修改会在所有的实例中反映出来,所以方法(引用类型)能够共用;
但是name是基本类型的值,基本类型值无法修改,每次都是重新创建变量(会在内存中新分配位置),赋值的同时就相当于在实例上创建属性了。

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'

缺点:
所有通过此函数继承的对象都共用原对象属性;和原型链继承相同;

5.寄生式继承

在原型式继承上进行了增强;除了拥有原对象上的方法和属性,还可以自定义自己的方法和属性;

//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'

缺点:
方法不能复用,降低效率,和构造函数类似;

6.寄生组合式继承

组合式继承会在SubType.prototype = new SuperType();SuperType.call(this,name);时分别第一次和第二次调用SuperType()构造函数,会降低效率;因此,使用寄生组合式继承:

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

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

//用此方法代替构造函数实例赋值的方法
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);
}

总结

构造对象的方法,普遍使用组合构造函数和原型链模式或动态原型模式,继承,则普遍使用组合式继承或寄生组合式继承
--整理自《Javasctipt高级程序设计》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容