JavaScript对象的创建和继承

1 篇文章 0 订阅

对象的创建

1.工厂模式

function person(name) {
    var obj = new Object();
    obj.name = name;
    obj.getName = function () {
        return "my name is " + this.name;
    }
    return obj;
}
var p = person("world");

优点:

  • 把一类对象的创建封装起来

缺点:

  • 不能识别对象类型
  • 还有若干其他方法的缺点,他也有

2.构造函数模式

function Person(name){
   this.name = name;
   this.getName = function(){
        return "my name is " + this.name;
   }
}
var p = new Person("world");

优点:

  • 可以通过instanceof判断对象类型
p instanceof Person;//true

缺点:

  • 原本应该属于共用部分,却各自占用一部分空间。如getName方法,理论上不同的Person对象都使用同一个getName方法;但实际上它们各有自己的getName方法,导致内存浪费。
var p1 = new Person("world");
var p2 = new Person("hello");
p1.getName === p2.getName;//false

解决方法:指向外部的方法(但没有更好的封装)

function Person(name){
   this.name = name;
   this.getName = getName;
}
function getName(){
   return "my name is " + this.name;
}

3.原型模型

function Person(){}
Person.prototype.name = "world";
Person.prototype.getName = function(){
   return "my name is " + this.name;
}
Person.prototype.friends = ["john","mary"];
var p1 = new Person();
var p2 = new Person();

优点:

  • 可以使用instanceof判断对象类型
  • 不同的对象共享同一份属性方法

缺点:

  • 不同对象默认属性的值相同
  • 所有的属性共享

对于基本类型的数据属性,修改时只是为对象添加该属性,查找属性时会先查找自身的属性再去找原型上的属性

p1.name = "new";
console.log(p1.name);//"new"  自身的属性
console.log(p2.name);//"world"  原型的属性

但对于引用类型的数据属性,修改该属性的内容时会影响其他对象

p1.friedns.push("ken");
console.log(p1.friedns);//["john","mary","ken"]
console.log(p2.friedns);//["john","mary","ken"]

4.组合使用构造函数模式和原型模式

function Person(name){
   this.name = name;
   this.friends = [];
}
Person.prototype.getName = function(){
   return "my name is " + this.name;
}
var p1 = new Person("hello");
var p2 = new Person("world");

优点:

  • 可以使用instanceof判断对象类型
  • 不同的对象共享一份理论上应该共享的属性
  • 不同的对象拥有属于自己的数据属性

缺点:

  • 公共属性不能封装起来,而是定义在构造函数外部

5.动态原型模式

function Person(name){
   this.name = name;
   this.friends = [];
   if(typeof this.getName!=="function"){
       Person.prototype.getName = function(){
           return "my name is " + this.name;
       }
   }
}
var p1 = new Person("hello");
var p2 = new Person("world");

优点:

  • 可以使用instanceof判断对象类型
  • 不同的对象共享一份理论上应该共享的属性
  • 不同的对象拥有属于自己的数据属性
  • 所有属性都封装在一起

6.寄生构造函数模式

function Person(name){
   var obj = new Array();
   obj.name = name;
   obj.getName = function(){
       return "my name is " + this.name;
   }
   return obj;
}
var p = new Person("world");

优缺点:和工厂模式差不多,不过可以为对象添加新的属性并通过函数封装起来(如为Array对象添加name属性)

7.稳妥构造函数模式

function Person(name){
   var obj = new Array();
   obj.getName = function(){
       return name;
   }
   return obj;
}
var p = Person("hello");

优缺点:和工厂模式差不多,不过可以保证原始数据安全,即只能通过getName方法获取name,却无法修改name(闭包的原理)

 


对象的继承

原型链继承

function Sup(name){
   this.name = name;
   this.friends = [];
}
Sup.prototype.getName = function(){
   return this.name;
}
function Sub(age){
   this.age = age;
}
Sub.prototype = new Sup("world");
Sub.prototype.getAge = function(){
   return this.age;
}
var sub = new Sub(21);
console.log(sub instanceof Sup);//true
console.log(sub.name);//"world"
console.log(sub.age);//21

优点:

  • 可以用instanceof判断继承关系
  • 继承父类的属性

缺点:

  • 对于父类的引用类型属性,其中一个对象修改后会影响其他的对象
  • 创建子类对象的时候,不能在不影响其他子类对象的情况下向父类构造函数传参(即创建子类对象时,继承自父类的属性的值一样且固定)

借用构造函数

function Sup(name){
   this.name = name;
   this.friends = [];
   this.getFriends = function(){
       return this.friends;
   }
}
Sup.prototype.getName = function(){
   return this.name;
}
function Sub(name,age){
   Sup.call(this,name);
   this.age = age;
}
var sub = new Sub("world",21);
console.log(sub instanceof Sup);//false
console.log(sub.getName);//undefined
console.log(sub.getFriends());//[]

优点:

  • 可以向父类构造函数传参
  • 每个子类实例都有自己的一份引用类型属性,修改不影响其他实例

缺点:

  • 只能继承父类在构造函数使用this定义的属性
  • 不能使用instanceof判断子类实例和父类的关系

组合继承

function Sup(name){
    this.name = name;
    this.friends = [];
}
Sup.prototype.getName = function(){
    return this.name;
}
function Sub(name,age){
    Sup.call(this,name);
    this.age = age;
}
Sub.prototype = new Sup();
Sub.prototype.constructor = Sub;
Sub.prototype.getAge = function(){
    return this.age;
}
var sub = new Sub("xin",21);
console.log(sub instanceof Sup);//true
console.log(sub.getName());//"xin"

优点:

  • 可以继承父类原型上的属性
  • 可以向父类构造函数传参
  • 可以使用instanceof判断子类实例和父类的关系
  • 子类实例独有一份继承于父类构造函数定义的属性

缺点:

  • 调用了两次父类的构造函数

原型式继承

function Sup(name){
    this.name = name;
    this.friends = [];
}
Sup.prototype.addFriend = function(f){
    this.friends.push(f);
}
function Sub(sup){
    function f(){}
    f.prototype = sup;
    return new f();
}
var sup = new Sup("xin");
var sub = Sub(sup);
sub.age = 21;
//使用Object.create
function Sup(name){
   this.name = name;
   this.friends = [];
}
Sup.prototype.addFriend = function(f){
   this.friends.push(f);
}
var sub = Object.create(new Sup("xin"),{
   age:{
      value:21
   }
});

优点:

  • 一个对象与另一个对象保持类似
  • 可以使用instanceof判断实例和Sup的关系

缺点:

  • 若使用同一个Sup对象,所有实例共享引用类型属性
  • 没有子类类型

寄生式继承

function object(o){
   function f(){}
   f.prototype = o;
   return new f();
}
function createObj(o){
   var clone = object(o);
   clone.say = function(){
      return "hello";
   };
   return clone;
}
var obj = {
   name:"xin",
   friends:[]
};
var ano = createObj(obj);

优点:

  • 不仅拥有了obj的属性,还添加属于自己的属性

缺点:

  • 自定义属性不能复用

寄生组合式继承

function object(o){
   function F(){}
   F.prototype = o;
   return new F();
}
function inheritPrototype(sub,sup){
   var proto = object(sup.prototype);
   proto.constructor = sub;
   sub.prototype = proto;
}
function Sup(name){
   this.name = name;
   this.friends = [];
}
Sup.prototype.addFriend = function(f){
   this.friends.push(f);
};
function Sub(name,age){
   Sup.call(this,name);
   this.age = age;
}
inheritPrototype(Sub,Sup);

优点:(修复了组合模式的缺点)

  • 只调用了一次Sup的构造函数
  • 避免了在Sub的原型上添加不必要的属性

组合继承是直接把子类的原型指向父类实例,从而多了父类的非原型属性

寄生组合式继承的原型链如下,子类的原型并非是父类的实例,故子类的原型不会有该实例的属性。由于使用call调用父类的构造函数,子类故实例拥有父类构造函数中定义的属性

  • 可以使用instanceof判断父类和子类实例的关系

 


基础原型图

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值