资讯专栏INFORMATION COLUMN

创建对象(一):创建与继承

microelec / 2208人阅读

摘要:创建实例的方式有三种对象字面量表示法操作符跟构造函数中的函数。下面主要讲的是最为复杂的操作符跟构造函数的创建对象实例的方法。

创建对象 一.创建对象的方法

理解原型对象:

无论什么时候,只要创建了新函数,就会根据一组特定的规则为该函数创建一个 prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性包含一个指向prototype属性的所在函数的指针。(重写原型属性会默认取消constructor属性)详细可见文章下图。

创建object实例的方式有三种:对象字面量表示法、new操作符跟object构造函数、(ECMAScrript5中的)object.create()函数。下面主要讲的是最为复杂的new操作符跟object构造函数的创建对象实例的方法。

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("NIcho",29,"software engineer");

工厂模式的问题:

工厂模式虽然解决了创建多个相似对象的问题,但是没有解决对象识别的的问题(即怎样知道一个对象的类型)。

1.2.构造函数模式
应用:创建对象的自有属性的主要方法,每定义一个函数就实例化了一个对象.
function Person(name,age,job){

    this.name=name;
    this.age=age;
    this.job=job;
    this.sayName=function(){
        alert(this.name);

}

//将构造的函数Person实例化,传入的参数作为自有属性,并且继承自Person.prototype
var person1=new Person(NIcho",29,"software engineer);
var person2=new Person(Jhon",26,"software engineer);

注意:1.alert(Person.name);//undefined
Person函数代替了createPerson函数,还有以下不同之处:
(1)没有显式创建对象
(2)直接将属性和方法赋给this对象(这里的this指的就是这个构造的函数)
(3)没有return 语句
我们在这个例子中创建的对象既是object的实例又是person的实例(所有对象均继承自object。object可以理解为对象的根原型,我们所用到的所有对象方法的操作,如toString 、substring 、splice等都是继承自object)

alert(person1 instanceOf Object);//true
alert(person1 instanceOf Person);//true

构造函数模式的问题:

使用构造函数的主要问题是,每个方法都要在每个实例上创建一次。person1和person2都有一个sayName的方法,但两个方法不是同一个Function实例(ECMAScript中的函数是对象,每定义一个函数就是实例化了一个对象)。

如果没有自定义向person原型中添加属性,实例化之后则得到Person自有属性的一个副本,并且继承object。(注:person1与person2中sayName()引用位置不同)

                         图1构造函数模式下的实例化对象

通过把函数定义转移到构造函数外来解决:

//全局函数
function sayName(){
    alert(this.name);
}

对应的:this.syName:sayName;

1.3原型模式
应用:用于定义实例共享的属性和方法

function Person(){
}
Person.prototype.name="Nicho";
Person.prototype.age=29;
Person.prototype.jod="software engineer";
Person.prototype.sayName=function(){alert(this.name)};
var person1=new Person();
person1.sayName();//"Nicho" 继承Person.prototype
var person2=new Person();
person2.sayName();//"Nicho"继承Person.prototype
alert(person1.sayName==person2.sayName);//true 引用位置相同

                            图2 person1、person2继承自person.prototype

(注:继承连接存在与实例与构造函数的原型之间而不是实例与构造函数之间)

关于原型上的一些函数:

1.检测obj是否为obj1的原型:obj.isPrototypeOf(obj1)//返回true/false
2.获得obj1的原型:object.getPrototypeOf(obj1)
3.检测一个属性是否存在于实例中(也就是自有属性)还是原型中(来自于继承):
Obj1.hasOwnProperty();//如果obj1有自有属性则返回true,否则返回false

1.4组合使用构造函数模式和原型模式
组合使用构造函数模式和原型模式既可以继承原型对象的属性,又可以设置自由属性

function Person(name,age,job){
        this.name=name;
        this.age=age;
        this.job=job;
}
Person.prototype={
    constructor:Person,
    sayName:function(){
        alert("Hi");
    }
   }
var person1=new Person(“Nicho”,29,”software engineer”);
//person1自有属性:name aga job;原型属性(来自继承):constructor sayName

代码读取某个对象的某个属性时的搜索方法:

搜索首先从对象实例开始,如果实例中找到了给定名字的属性,则返回该属性的值。如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找给定名字的属性。如果在原型对象中找到了该属性,则返回属性的值,否则一直向上查找给定名字的属性,直到object。如果没有找到则返回undefined。

这就需要我们注意:虽然我们可以通过对象实例访问原型中的值,但却不能通过对象实例(注意!!这里说的是通过对象实例而不是在原型中修改属性)来重写原型中的值。如果在实例中添加了一个与实例原型中同名的属性,该属性将会屏蔽原型中的那个属性。来看下面的的例子:

function Person(){

}
Person.prototype.name="Nicho";
Person.prototype.age=29;
Person.prototype.jod="software engineer";
Person.prototype.sayName=function(){alert(this.name)};
var person1=new Person();
var person2=new Person();
 person1.name="Greg";
alert(person1.name);//Greg-来自实例
alert(person2.name);//Nicho-来自原型;
//person1对属性 name的修改并不能修改原型上的相应属性,因此person2继承自原型

1.person1=new Person()与 person3=person1是不同的:前者是实例化一个对象,
后者遵循复制函数(对象引用)的原理
2.对对象整体进行操作(引用)和对对象中的某个属性(值)进行操作实现原理是不同的
1). 整个对象的复制即引用相同

var person3=person1;//整个对象的复制即引用相同(指针指针指针!只是一个索引,实际存储都不在指针位置下)
person3.name=”newName”;//修改属性名称的值
alert(person1.name);//newName;//另一个来自此处的引用属性的值会修改;即复制对象原理
alert(person1.isPrototypeOf(person3));//false
alert(Person.prototype.isPrototypeOf(person3));//true

2).简单的属性的复制,并非引用同一个属性值.

var person1=new Person("NIcho",29,"software engineer");
  var person2=new Person("HIcho",9,"Hardware engineer");
  person1.name=person2.name;//简单的属性的复制,并非引用同一个属性值
  person2.name="NEW";//尝试赋新值
  alert(person1.name);//HIcho,说明是值的传递而并非引用
二.继承

1.ECMAScript只支持实现继承,而且实现继承主要是依靠原型链来实现的。
2.其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。(即:原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型来实现的)

2.1原型链的继承

function superType(){
    this.property=true;
   }
    superType.prototype.getSuperValue=function(){
        return this.property;
    };
    function subType(){
        this.subProperty=false;
    }
    //继承superType
    subType.prototype=new superType();
    subType.prototype.getSubValue=function(){
        return this.subProperty;
    };
    var instance=new subType();
    alert((instance.getSuperValue)());//true  getSuperValue来自superType.prototype
    alert(instance.property);//true   property来自superType实例
    

实现的本质是重写原型对象,代之以一个新类型的实例。换句话说,原来存在于superType的实例的所有属性和方法(包括自有属性和继承属性),现在也存在于subType.prototype中了。

                        图4 subType继承了superType 

(此时instance.constructor现在指向的是superType,因为subType的原型指向了另一个对象superType的原型,而这个原型对象的constructor指向的是superType)

注意:
1.别忘记默认的对象Object.prototype: subType继承了superType,superType继承了Object.prototype

确定原型和实例的关系:

(1)instanceOf;测试实例与原型链中出现过的构造函数

alert(instance instanceOf  superType);//true
alert(instance instanceOf  subType);//true

(2)isPrototypeOf:只要原型链中出现过的原型都可以说是该原型链所派生出来的实例的原型

alert(superType.protoType.isPrototypeOf (instance));//true
alert(subType.protoType.isPrototypeOf (instance));//true

原型链的问题:
在通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章地变成了现在的原型属性了。

2.2解决原型链继承带来的问题的方法

解决这个问题的技术是借用构造函数,使用最多的继承模式是组合继承。此外还有原型式继承,寄生式继承,寄生组合继承。这里主要讲借用构造函数和组合继承。

(1)借用构造函数
//在子类型构造函数的内部调用超类型的构造函数

function superType(){
    this.colors=["red","blue","green"];
  }
  function subType(){
    //继承了superType
    superType.call(this);//在新创建的subType实例的环境下调用superType函数
    //这样一来,就会在新subType对象上执行superType函数中定义的所有初始化代码
    //结果subType的每一个实例都会具有自己的color属性的副本了
  }
  var instance1=new subType();
  instance1.colors.push("black");
  alert(instance1.colors);//red,blue,green,black
  var instance2=new subType();
  alert(instance2.colors);//red,blue,green

(2)组合继承
思路是使用原型链实现对属性和方法的继承,而通过借用函数来实现对实例属性的继承

function superType(name){//超类中定义,为以后继承该实例属性(子类的原型)做准备
    this.name=name;
    this.colors=["red","blue","green"];
  }
superType.prptotype.sayName=function(){
alert(this.name);
}// superType.prptotype新增属性,位于继承的更高层
function subtype(name,age){//创建自有属性
superType.call(this,name); //继承属性式的创建
this.age=age;//直接新增
}
//继承方法
subtype.prototype=new superType();
subtype.prototype.constructor=subtype;
subtype.prototype.sayAge=function(){//subtype.prototype为自己新增方法
alert(this.age);
}
var instance1=new subType(“Nicho”,29);
  instance1.colors.push("black");
  alert(instance1.colors);//red,blue,green,black
instance1.sayName();//”Nicho”
instance1.sayAge();//29

  var instance2=new subType(“Greg”,27);
  alert(instance2.colors);//red,blue,green
instance2.sayName();//”Greg”
instance2.sayAge();//27

总结:
1.subType的自有属性: this.age=age

2.subType的继承属性:
(1)superType.prptotype.sayName 、subtype.prototype.sayAge
(2)function superType(name){

this.name=name;
        this.colors=["red","blue","green"];
          }

3.
下面让我们看下一种情况:重写原型对象(完全重写而不是简单的添加原型对象的属性)
我们知道,在调用构造函数是会为实例添加一个prototype指针,而把这个原型修改为另一个对象就等于切断了构造函数与最初原型之间的联系。请记住:实例中的指针仅指向原型而不指向构造函数。

function Person(){};
var friend=new Person();
Person.prototype={
Constructor:Person,
Name:“Nicholas”,
Age:29,
sayName=function(){
alert(“this.name”);
}
}
friend.sayName();//error

重写原型对象之前:

重写原型对象之后:

由图可见:重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系,它们引用的仍是最初的原型

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79098.html

相关文章

  • 彻底理解Javascript中的原型链继承

    摘要:在节中,我们学习到了通过构造函数创建对象的三个重要步骤,其中的一步是把构造函数的对象设置为创建对象的原型。利用而不是直接用创建一个实例对象的目的是,减少一次调用父构造函数的执行。 JavaScript语言不像面向对象的编程语言中有类的概念,所以也就没有类之间直接的继承,JavaScript中只有对象,使用函数模拟类,基于对象之间的原型链来实现继承关系,ES6的语法中新增了class关键...

    ziwenxie 评论0 收藏0
  • 前端进击的巨人(七):走进面向对象,原型原型链,继承方式

    摘要:除了以上介绍的几种对象创建方式,此外还有寄生构造函数模式稳妥构造函数模式。 showImg(https://segmentfault.com/img/remote/1460000018196128); 面向对象 是以 对象 为中心的编程思想,它的思维方式是构造。 面向对象 编程的三大特点:封装、继承、多态: 封装:属性方法的抽象 继承:一个类继承(复制)另一个类的属性/方法 多态:方...

    wums 评论0 收藏0
  • ES5-对象创建继承----《JavaScript高程3》

    摘要:并且,在创建子类型的实例时,无法向超类型的构造函数传递参数。借用构造函数经典继承在子类型构造函数的内部调用超类型构造函数。缺点是同样具有构造函数模式创建对象的固有弊端构造函数中烦人方法函数对象重复创建。 创建对象的几种方式 在逻辑上从低级到高级:工厂模式、构造函数模式、原型模式、组合模式。当然还有其他模式,但是这四者逻辑关系强,总结起来很有感觉。之所以和继承一起分析,也是因为逻辑关系很...

    Heier 评论0 收藏0
  • JavaScript系列--浅析原型链继承

    摘要:综上所述有原型链继承,构造函数继承经典继承,组合继承,寄生继承,寄生组合继承五种方法,寄生组合式继承,集寄生式继承和组合继承的优点于一身是实现基于类型继承的最有效方法。 一、前言 继承是面向对象(OOP)语言中的一个最为人津津乐道的概念。许多面对对象(OOP)语言都支持两种继承方式::接口继承 和 实现继承 。 接口继承只继承方法签名,而实现继承则继承实际的方法。由于js中方法没有签名...

    draveness 评论0 收藏0
  • 面向对象的小九九

    摘要:由构造函数返回的对象就是表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤创建的对象。运算符返回一个布尔值,表示对象是否为某个构造函数的实例。 面向对象 本人能力有限,有误请斧正 本文旨在复习面向对象(不包含es6) 本文学习思维 创建对象的方式,获取对象属性 构造函数,构造函数的new 做了什么 原型与原型对象 原型链 继承(借用构造继承、原型继承、组合继承、寄生组合继承)...

    时飞 评论0 收藏0

发表评论

0条评论

microelec

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<