Javascript中创建对象的几种方式

一、一般来说,最简单的创建方式就是字面量方式

var animal = {
  name:'animal',
  speed:12,
  run:function(){ 
       alert(this.speed)       
     }
}

或是创建一个Object实例,再给他添加属性和方法

var animal = new Object()
animal.name = 'animal'
animal.speed = '12'
animal.run = function(){
  alert(this.speed)    
}

缺点:很明显,当需要创建多个对象时,会有大量的代码重写出现,为了解决这个问题,工厂模式是个很好的办法。

二、 工厂模式

用函数封装实现创建对象具体细节的接口,如:

function createAnimal(name,speed){
    var obj = new Object()
    obj.name = 'animal'
    obj.speed = '12'
   obj.run = function(){
      alert(this.speed)    
    }
return obj
}

函数createAnimal 根据传入的具体参数来构建一个包含必要信息的animal对象,每次调用该函数,只需要传入必要的参数即可。
缺点:每个创建出来的对象的类型都是Object,不能判断其归属对象

三、构造函数模式

创建自定义的构造函数,然后可以设置自定义对象类型的属性和方法,如,

function Animal(name,speed){
    this.name = name
    this.speed = speed
    this.run = function(){
        alert(this.speed)
    }
}
var animal1 = new Animal('老虎',120)

注:构造函数以大写字母开头,非构造函数以小写字母开头
新创建的对象都会有一个constructor(构造函数)属性,用来标明它的对象类型

alert(animal1.constructor == Animal)//true

但检验对象类型时,一般用instanceof操作符

alert(animal1 instanceof Object)//true
alert(animal1 instanceof Animal)//true

这说明例子里创建出来的对象既是Object的实例,又是Animal的实例
缺点:每次实例化时,实例上的方法就会重新创建一遍。相当于每次都创建一个Function的实例,然而创建多个完成相同任务的Function实例完全没有必要,所以我们可以吧函数的定义放到构造函数外面来解决这个问题。

function Animal(name,speed){
    this.name = name
    this.speed = speed
    this.run = run
}
function run(){
  alert(this.speed)
}

将run函数的定义设置在构造函数之外,也就是全局定义下的函数,由于run函数里包含了一个指向函数的指针,解决了多个函数做一件事的问题,担心的问题又来了,虽然run函数定义在全局,但他只应该被Animal调用,应该是局部的函数,但现在其他的对象函数都可以调用它,这就暴露了构造函数的内部方法,毫无封装性可言,原型模式正好可言解决这个问题。

四、原型模式

我们每次创建的函数都会有一个默认prototype(原型)属性,它是一个指向原型对象的指针,原型对象就是包含构造函数里所有实例共享的属性和方法的对象。所以通过使用原型对象可以实现属性和方法的共用,例:

function Animal(){
}
Animal.prototype.name = "老虎"
Animal.prototype.speed = "120"
Animal.prototype.run = function(){
    alert(this.speed)
}
var  animal1 = new Animal()
animal1.run()//120
var animal2 = new Animal()
animal.run()//120

以之前的Animal构造函数和Animal.prototype创建实例的代码为例,下图展现了各个对象之间的关系。


image

简单来说,在创建了自定义的构造函数后,该函数会自动获得一个prototype属性,这个属性指向的就是它的原型对象。而在默认情况下,所有的原型对象也都会自动获得一个constructor(构造函数)属性,该属性指向的是该原型对象的构造函数。即Animal.prototype.constructor = Animal

判断一个属性或是方法是否存在于原型当中(isPrototypeOf())

alert(Animal.prototype.isPrototypeOf(animal1))//true
ES5中增加了个新方法,Object.getPrototypeOf()用于获得一个对象的原型
alert(Object.getPrototypeOf(animal1) == Animal.prototype)// true
alert(Object.grtPrototypeOf(animal1).name) //'老虎'

如果我们需要修改原型中的值,通过实例重写是不可以的,我们只会在实例中创建一个名称相同的属性,同时屏蔽掉原型中的那个属性,我们可以试着去验证看看,

var animal1 = new Animal()
var animal2 = new Animal()
animal1.name = "狮子"
alert(animal1.name)''狮子" --实例中新增的属性
alert(animal2.name)// "老虎" -- 原型中的属性

所以很自然的我们可以发现实例属性读值的顺序:先在实例属性当中搜索该属性,如果存在,就返回该值;如果没有,就去实例的原型当中搜索。

所以如果我们需要获取原型当中的属性,那么实例中就不可以有该属性,即便该属性的值设为null,但这个属性依然存在于实例当中,我们也无法访问原型中的该属性。delete操作符可以彻底删除实例中的该属性,我们也就可以自然的访问到原型当中的该属性了。例:继上面例子

delete animal1.name
alert(animal1.name)// "老虎"--原型当中的属性

那当我们获得了一个属性时,我们想知道它是来自实例还是来自原型时,该怎么判断呢?

hasOwnProperty()方法就可以做到,它可以检测一个属性是来自实例还是原型,只有当给定属性来自于对象的实例中它才回返回true。

var animal1 = new Animal()
var animal2 = new Animal()
alert(animal1.hasOwnProperty("name"))//false
animal1.name = "狮子"
alert(animal1.name)''狮子" --实例中新增的属性
alert(animal1.hasOwnProperty("name"))//true
alert(animal2.name)// "老虎" -- 原型中的属性
alert(animal2.hasOwnProperty("name"))//false

但问题来了,该怎么判断一个属性是否是存在于原型链当中的呢?仅凭hasOwnProperty()返回值为false并不能判断,因为这个属性可能既不在实例也不在原型当中。
我们可以使用in操作符,in操作符会在通过对象能够访问到给定属性时返回true,无论该对象是否存在于实例中还是原型中。使用hasOwnProperty()方法和in操作符,就可以确定该属性是否存在于原型当中,

function hasPrototypeProperty(obj,name){
    return ?obj.hasOwnPreperty(name) && (name in obj)
}

原型 -- 字面量创建

在原型创建对象时,我们可以发现每次添加属性和方法时,都要重复敲Animal.prototype。为了减少代码重复量(省事),也从视觉上更好的封装原型的功能,我们可以用一个包装了所有属性和方法的对象字面量来重写整个原型对象,如:

function Animal(){
}
Animal.prototype = {
    name : "老虎",
    speed:120,
    run : function(){
        alert(this.speed)
   }
}

虽然我们将Animal.prototype设置为等于一个以字面量形式创建的新对象,最终结果相同,但有一个问题,constructor不再指向Animal,而是Object。我们可以手动设置constructor属性

Animal.prototype = {
    constructor:Animal,
    name : "老虎",
    speed:120,
    run : function(){
        alert(this.speed)
   }
}

缺点:①没有为构造函数传递参数初始化,所有的实例默认获得相同的属性值
②共享特性,由于所有的原型属性都被实例共享,如果某个实例需要更改其中一个属性,其他所有实例的该属性值也会被修改,就不一定是我们想要的结果了。

五、构造函数和原型模式组合使用

构造函数模式用于定义实例属性,原型模式用于定义共享的方法和属性,这样每个实例都有自己单独的实例属性,同时又有对共享方法属性的引用。

function Animal(name,speed){
    this.name = name
    this.speed = speed
    friends = ["one","two"]
    this.run = function(){
        alert(this.speed)
    }
}
Animal.prototype = {
    constructor : Animal
    family:["tom","jery"]
}
var animal1 = new Animal("老虎",120)
var animal2 = new Animal("狮子",130)
animal1.frirends.push("three")
alert(animal1.friends)// ["one","two","three"]
alert(animal1.friends)// ["one","two"]

动态原型模式

构造函数初次调用时,通过判断某个属性或方法是否存在,来决定是否需要初始化原型

function Animal(name,speed){
   this.name = name
   this.speed = speed
   friends = ["one","two"]
   if(typeof this.run != 'function'){
       Animal.prototype.run= function(){
           alert(this.speed)
       }
   }
}

七、寄生构造函数

除了使用new操作盒并把包装函数称为构造函数外,该模式和工厂模式一模一样,他的作用也仅仅是封装创建对象的代码。

function Animal(name,speed){
    var obj = new Object()
   obj.name = name
   obj.speed = speed
   obj.run = function(){
        alert(this.speed)
    }
return obj
}

注:寄生构造函数模式返回的对象与构造函数和构造函数的原型没有关系,所以不能依靠instanceof操作符来确定对象类型

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

推荐阅读更多精彩内容