JavaScript基础概念之----面向对象----创建对象(深入原型及原型链)

一、使用Object构造函数或对象字面量创建对象

var person = new Object()
person.name = 'adhehe';
person.age = 23;
//...
// 
var person = {
  name:'adhehe',
  age:23,
  //...

}

缺点:使用同一个接口创建很多的对象,会产生大量的重复代码。

二、工厂模式

这种模式抽象了创建具体对象的过程。用函数来封装以特定接口创建对象的细节。

function createPerson(name,age){
  //将创建的具体细节封装起来,最后返回一个对象
var o = new Object(); o.name = name; o.age = age; o.getName = function(){ return this.name; } return o; } var person1 = createPerson('adhehe',23); var person2 = createPerson('Jhone',45);

缺点:虽然解决了创建多个相似对象的问题,但没有解决 对象识别的问题,即怎么知道一个对象的类型

三、构造函数模式

构造函数可以用来创建特定类型的对象。如Object、Array等。也可以创建自定义构造函数。

function Person(name,age){
    this.name = name;
    this.age = age;
    this.getName = function(){
        return this.name;
    }
}    

var person1 = new Person('adhehe',23);
var person2 = new Person('Jhone',45);

以new关键字创建Person的实例,经历以下4个步骤:

  • 创建一个新对象
  • 将构造函数的作用域赋给了新对象(即this指向了这个新对象)
  • 执行 构造函数中的代码(即为新对象添加属性)
  • 返回新对象

instanceof 操作符 可以检测对象的类型

//以person为例

console.log(person1 instanceof Person)
console.log(person2 instanceof Person)
console.log(person1 instanceof Object)
console.log(person2 instanceof Object)
//都输出 true

构造函数与普通函数的唯一区别是:调用的方式不同。只要通过new操作符调用,它就是构造函数。

缺点:每个方法都要在每个实例上重新创建一遍。

//以上面 person为例

person1.getName == preson2.getName
//false

 

四、原型模式

创建的每个函数都有一个prototype(原型)属性,它是一个指针,批向一个对象,而这个对象的用途就是:包含可以共享的属性和方法,给那些特定类型的所有实例。

function Person(){}

Person.prototype.name = 'adhehe'
Person.prototype.age = 23
Person.prototype.getName = function(){
  return this.name;
}

var preson1 = new Person()
var preson2 = new Person()

console.log(person1.getName())
console.log(person2.getName())
//都返回 adhehe

person1.getName == person2.getName
//true

 1、理解原型对象

只要创建了函数,就会为该函数创建一个prototype属性,这个属性指向函数的原型对象。

所有原型对象都会自动获得一个constructor属性(构造函数),这个属性指向prototype属性所在的函数的指针。

当调用构造函数创建一个实例后,实例内部将包含一个指针(__ptoto__),指向构造函数的原型对象。

Person.prototype.constructor == Person

person1.constructor == Person
person2.constructor == Person

person1.__proto__ == Person.prototype
person2.__proto__ == Person.prototype

isPrototypeOf()方法 确定对象之间是否存在原型关系

Person.prototype.ifPrototypeOf(person1) //true
Person.prototype.ifPrototypeOf(person2) //true

Object.getPrototypeOf()方法 是ECMAScript5新增的检测方法

Object.getPrototypeOf(person1) == Person.prototype //true
Object.getPrototypeOf(person2) == Person.prototype //true

每当读取某个对象中的某个属性时,首先搜索该对象本身,再搜索该对象指向的原型对象。如果在实例对象中某个属性名与原型中的一个属性同名,该属性将会屏蔽原型中的属性。

function Person(){}

Person.prototype.same = 1;

var person1 = new Person()
var person2 = new Person()
person1.same
= 2; console.log(person1.same) //2 console.log(person2.same) //1

使用delete操作符 可以完全删除实例属性,从而能够重新访问原型中的属性。

//接上例

delete person1.same;
console.log(person1.same) //1

hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。

person1.hasOwnProperty('name') // true
person1.hasOwnProperty('hello') //false

2、in 操作符

有两种方式使用:

  • 单独使用
  • for-in

单独使用:通过对象能够访问指定属性时返回true,无论该属性存在于实例还是原型中。

"name" in person1 //true

同时使用 hasOwnProperty()方法和 in 操作符,就可以确认该属性到底是存在于对象中,还是存在于原型中。

function has(obj,name){
    return !obj.hasOwnProperty(name) && (name in obj);
}

//in为true,hasOwnProperty()返回false,就可以确定属性是在原型中

for-in 循环:返回所有能够通过对象访问的、可枚举的属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性。屏蔽了原型中不可枚举的属性的实例属性也会在for-in循环中返回。

Object.keys()方法 可以取得对象上所有可枚举的实例属性。

function Person(){}

Person.prototype.name = 'adhehe'
Person.prototype.age = 23
Person.prototype.getName = function(){
  return this.name;
}

var keys = Object.keys(Person.prototype)
console.log(keys) // "name,age,getName"

var person1 = new Person()
person1.name = 'Rob'

var person1Keys = Object.keys(person1)
console.log(person1Keys) //"name"

Object.getOwnPropertyNames()方法 可以得到所有实例属性,无论它是否可枚举。

var keys = Object.getOwnPropertyNames(Person.prototype);
console.log(keys)
//"constructor,name,age,getName"

3、更简单的原型语法

function Person(){}

Person.prototype = {
    name:'adhehe',
    age:23,
    getName:function(){
        return this.name
    }
}

在这里,constructor属性不再指向Person。可以 将constructor属性值设置回适当的值

function Person(){}

Person.prototype = {
    constructor:Person,
    name:'adhehe',
    age:23,
    getName:function(){
        return this.name
    }
}

以上面这种方式重设constructor属性会导致它的Enumerable特性被设置为true。默认情况下原生的constructor是不可枚举的。可以使用Object.defineProperty()修改

 

function Person(){}

Person.prototype = {
    name:'adhehe',
    age:23,
    getName:function(){
        return this.name
    }
}

Object.defineProperty(Person.prototype,'constructor',{
    enumberable:false,
    value:Person
})

 

4、原型的动态性

对原型对象的修改都能够立即 从实例上反映出来。可以随时为原型添加属性和方法。

var o = new Person();

Person.prototype.sayHi = function(){
    //...
}

o.sayHi()  //没有问题

但是,如果重写原型对象,则会切断 构造函数与最初原型之间的联系。实例中的指针仅指向原型,而不指向构造函数。

function Person(){}

var o = new Person();

Person.prototype = {
    constructor:Person,
    name:'adhehe',
    age:23,
    getName:function(){
        //....
    }
}

o.getName() // 报错

5、原生对象的原型

所有原生的引用类型,都在其构造函数的原型上定义了方法。

我们也可以在原生对象的原型上定义新的方法或修改原型,但不推荐这么做。

缺点:

  • 省略了为构造函数传递初始化参数的环节,导致所有的实例在默认情况下都将取到相同的属性值
  • 由于原型的共享本性的原因,在其中一个实例对象上添加或修改了原型属性的值,其他实例对象都会被修改
function Person(){}

Person.prototype = {   
    constructor:Person,
    name:'adhehe',
    age:23,
    friends:['a','b'],
    getName:function(){
        //...
    }
}

var o1 = new Person()
var o2 = new Person()

o1.friends.push('c')

console.log(o1.friends) //a,b,c
console.log(o2.friends) //a,b,c
//两个对象实例的friends返回了相同的值

五、构造函数模式 与 原型模式 组合

 

  • 构造函数模式用于定义实例属性
  • 原型模式用于定义方法和共享的属性
function Person(name,age){
    this.name = name;
    this.age = age;
    this.friends = ['a','b']
}

Person.prototype = {
    constructor:Person,
    getName:function(){
        //...
    }
}

var o1 = new Person('adhehe',23);
var o2 = new Person('Greg',45);

o1.friends.push('c');

console.log(o1.friends)  //a,b,c
console.log(o2.friends)  //a,b
console.log(o1.friends === o2.friends)  //false
console.log(o1.getName === o2.getName)  //true

六、动态原型模式

在构造函数中初始化原型(仅在必要的情况下),它保持了同时使用构造函数和原型的优点。

它只会在初次调用构造函数时才会执行,此后,原型已经完成初始化,不需要再做什么修改。

使用动态原型模式时,不能使用对象字面量重写原型。会切断现有实例与新原型之间的联系。

function Person(name,age){
    this.name = name;
    this.age = age;

    if(typeof this.getName != 'function'){
        Person.prototype.getName = function(){
            //...
        }
    }
}

var o = new Person('adhehe',23)
o.getName()

七、寄生构造函数模式

创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。

这个模式与工厂模式是一模一样的,不同的是这里使用 new 操作符创建实例对象,并把包装函数叫作构造函数。

function Person(name,age){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.getName = function(){
        //...
    }

    return o;
}

var person1 = new Person('adhehe',23)
person1.getName();

缺点:返回的对象与构造函数或构造函数的原型属性之间没有任何关系,所以不能依赖 instanceof 操作符来确定对象类型。在有其他模式可用的情况下,不推荐使用这种模式

八、稳妥构造函数模式

稳妥对象:指的是没有公共属性,其方法也不引用 this 的对象。

稳妥对象 最适合在一些安全的环境中,或者在防止数据被其他应用程序改动时使用。

此模式 与 寄生构造函数类似的模式,有两点不同:

  • 新创建对象的实例方法不引用 this
  • 不使用 new 操作符调用构造函数
function Person(name,age){
    var o = new Object();

    //在这里定义私有变量与属性

    o.getName = function(){
        return name;
    }

   return o;
}

var person1 = Person('adhehe',23)
person1.getName() //adhehe

//在这种模式创建的对象中,除了使用getName()方法之外,没有其他办法可以访问name

缺点:与寄生构造函数模式类似,返回的对象与构造函数或构造函数的原型属性之间没有任何关系,所以不能依赖 instanceof 操作符来确定对象类型。

 

转载于:https://www.cnblogs.com/adhehe/p/9787725.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值