主要摘自《Javascript高级程序设计》

创建对象的方法

  • 工厂模式
  • 构造函数
  • 原型链
  • 组合(构造函数和原型链)
  • 寄生构造函数模式
  • 委托构造函数模式

工厂模式

var person = function(name,age){var o = new Object();o.name = name;o.age = age;o.sayName = function() {console.log(this.name)}return o;
} var person1 = person('li', 20);
var person2 = person('xiao', 18);person1.sayName();  //li
person2.sayName();  //xiao
复制代码
  • 先创建一个object
  • object绑定属性和方法
  • 返回这个object

缺点

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

构造函数

var Person = function(name,age) {this.name = name;this.age = age;this.sayName = function() {console.log(this.name)}
}var person1 = new Person('li',20)
var person2 = new Person('xiao',18)person1.sayName()  //li
person2.sayName()  //xiao
复制代码

构造函数和工厂函数不同之处在于:

  • 没有显示的创建对象
  • 直接将属性和方法赋给了this对象
  • 没有return语句
  • 函数名的首字母大写
  • 要创建构造函数的实例,必须使用new操作符

** 调用构造函数会经历下面4个步骤(即new干了什么)**:

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

上面的person1和person2分别保存Person的一个不同实例,这两个对象都有一个constructor(构造函数)属性,该属性执行Person

person1.constructor == person2.constructor == Person

可以用constructor来识别对象类型,但是用instanceof 更可靠

person1和person2同时也是object的实例,因为所有的对象都继承自Object

缺点

如果构造函数里面有方法,那方法都会在每个实例上都创建一遍,因此不同实例上的函数是不相等的

person1.sayName == person2.sayName // false

虽然可以把方法移到构造函数外部,设置成全局函数,但是如果对象需要定义很多方法,就要定义很多个全局函数,

原型模式

function Person(){};
Person.prototype.name = "li";
Person.prototype.age = 20;
Person.prototype.sayName = function(){console.log(this.name)
};
var person1 = new Person()
var person2 = new Person()person1.sayName()  //li
person2.sayName()  //liperson2.name = 'xiao'  //person2这个实例加了一个name属性,重写name属性,但不会改变原型的值
person1.sayName()  //li
person2.sayName()  //xiao  //如果实例上有name属性,取的是实例上的
复制代码

好处

可以让所有对象实例共享它所包含的属性和方法。

person1.sayName !== person2.sayName //true

原型对象

只要创建了一个函数,就会为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针(妈呀,很拗口,看代码和图比较直观)

创建了自定义的构造函数后,其原型对象默认只会取得constructor属性,至于其他方法,都是从object继承而来。当调用构造函数创建一个实例,该实例内部将包含一个指针,指向构造函数的原型对象,可以用_proto_属性访问。这个链接存在与实例与构造函数的原型对象之间,而不存在于实例与构造函数之间。

Person.prototype.constructor = Person

person1._proto_ == Person.prototype

判断是不是对象的原型

  • isPrototypeOf

Person.prototype.isPrototypeOf(person1) // true

  • Object.getPrototypeOf可以取到一个实例的原型

Object.getPrototypeOf(person1) == Person.prototype // true Object.getPrototypeOf(person1).name //li

判断一个属性是存在实例中还是原型中

  • hasOwnProperty()只在给定属性存在实例中时,才返回true
person1.hasOwnProperty('name') // false
person2.hasOwnProperty('name') //true
复制代码
  • in 不管属性是存在实例上还是原型中,都返回true

'name' in Person // true

hasOwnProperty和in一起可以判断属性是不是在原型中

!object.hasOwnProperty(name) && ('name' in object)

对象字面量

我们将Person.prototype 设置为等于一个以对象字面量形式创建的新对象,最终结果相同,但是constructor属性不再指向Person。这里的语法,本质上完成重写了默认的portotype对象,因此它的原型是Object,constructor是指向Object的构造函数。但是用instanceof 操作符还能返回正确结果

function Person(){};
Person.prototype = {name: 'li',age: 20,sayName: function(){console.log(this.name)}
};
var person1 = new Person()
console.log(Person.prototype.constructor == Person)  //false
console.log(Person.prototype.constructor == Object)  //true
console.log(person1 instanceof Person) // true
复制代码

原型的动态性

function Person(){};
var person1 = new Person()
Person.prototype = {name: 'li',age: 20,sayName: function(){console.log(this.name)}
};person1.sayName()  //person1.sayName is not a function
复制代码

先创建实例,在重写其原型对象,会切断现有原型与之前任何已经存在的对象实例之间的联系。person1引用的还是原来的原型。可以用下面这种写法

function Person(){};var person1 = new Person()Person.prototype.sayName = function(name){console.log(name)}person1.sayName('li')  //li
复制代码

原型对象的缺点

  • 省略了为构造函数传递初始化参数,所有实例在默认情况下都取得相同的属性值。
  • 如果有引用类型的属性,会导致在一个实例里修改引用类型的值,会把原型上的也修改了

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

  • 构造函数模式用于定义实例属性
  • 原型模式用于定义方法和共享的属性
var Person = function(name,age) {this.name = name;this.age = age;
}Person.prototype.sayName = function(){console.log(this.name)
};
var person1 = new Person('li',20)
var person2 = new Person('xiao',18)person1.sayName()  //li
person2.sayName()  //xiao
复制代码
  • 动态原型模式
function Person(name, age){this.name = name;this.age = ageif(typeof this.sayName != function) {Person.prototype.sayName = function(name){console.log(this.name)}}
};
复制代码

判断这个代码只会在初次调用构造函数时执行

  • 寄生构造函数模式
  • 委托构造函数模式 不引用this

继承

js只支持实现继承 主要是依靠 原型链 来实现的.

利用原型让一个引用类型继承另一个引用类型的属性和方法

方法:

  • 原型链
  • 借用构造函数
  • 组合继承
  • 原型式继承
  • 寄生式继承
  • 寄生组合式继承

原型链

每一个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例都包含一个指向原型对象的指针。那么,如果我们让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针,相应的,另一个原型中也包含着一个指向另一个构造函数的指针,假如另一个原型又是另一个类型的实例。。。如此层层递进,就构成了实例和原型的链条

如果引用对象(实例instance)的某个属性,会现在这个对象内找,如果找不到,就会到这个对象的原型上去找,原型上找不到,就到原型的原型上去找,直到找到这个属性或者返回null为止

借用构造函数

在子类型构造函数的内部调用超类型构造函数

缺点

无法复用函数,所有类型只能使用构造函数模式

组合继承

原型链和借用构造函数组合,使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承

缺点

调用两次超类型构造函数,

原型式继承

function object(o) {function F(){};F.prototype = o;   //F的原型指向传入的对象oreturn new F()
}
复制代码

Object.create(),这个方法接收两个参数,一个是用作新对象原型的对象和(可选)一个为新对象定义额外属性的对象,只有一个参数时,Object.create()与Objec()一样

包含引用类型值的属性始终都会共享相应的值

寄生式继承

创建一个仅用于封装继承过程的函数。不能做到函数复用

寄生组合式继承

通过借用构造函数来实现对实例属性的继承,通过原型链的混成模式来继承方法。仅调用一次超类型构造函数,避免创建不必要的多余的属性

function Parent(name) {this.name = name;this.colors = ['yellow']
}Parent.prototype.sayName = function() {console.log(this.name)
}function Children(name,age) {Parent.call(this,name);this.age = age
}function inheritPrototype(parent,children) {var prototype = Object(parent.prototype)prototype.constructor = children;children.prototype = prototype
}inheritPrototype(Parent, Children);
var instance1 = new Children('li', 20)
instance1.sayName()
复制代码

es6的继承和es5有什么不同

ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

如果子类没有定义constructor方法,这个方法会被默认添加,在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例。

转载于:https://juejin.im/post/5cf639fbe51d4556bb4cd343

创建对象、原型、原型链相关推荐

  1. JS难点剖析-原型原型链

    2019独角兽企业重金招聘Python工程师标准>>> js的原型&原型链&闭包,在很多人看来是区分JS程序员水平的关键知识点,当然对这句话我不是十分赞同.但是掌握这 ...

  2. JavaScript——原型/原型链中的顶层对象(图解)

    本篇需要提前了解的知识储备:原型和原型链,以下仅概要介绍,不作详细讲解. 详细学习可参考: JavaScript--面向对象(封装):https://blog.csdn.net/qq_29493173 ...

  3. js面向对象的程序设计 --- 中篇(创建对象) 之 原型模式

    ·原型模式 我们创建的每一个函数都由一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有 实例共享的属性和方法. 如果按照字面意思来理解,那 ...

  4. js原型和原型链_初识JS原型/原型链/原型继承

    本文思路: 原型->原型链->原型继承->基于原型的继承/ES6中的继承->__proto__与prototype 首先我们知道JS中有对象,比如: var 但是在我们没有对这 ...

  5. Javascript之创建对象(原型模式)

    我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,它的用途是包含可以有特定类型的所有实例共享的属性和方法. prototype就是通过构造函数而创建的那个对象 ...

  6. 我所理解的原型原型链

    一.题目 我们先看一道题目 var F = function() {}; Object.prototype.a = function() {console.log('a');};Function.pr ...

  7. 我所理解的原型原型链 1

    一.题目 我们先看一道题目 var F = function() {}; Object.prototype.a = function() {console.log('a');};Function.pr ...

  8. 如何更加简单的理解JS中的原型原型链概念

    前面写了很多关于前端经验之谈,今天就来点干货吧.这篇文章将会介绍原型这个概念 原型是整个Javascript中比较重要的概念,如果面向对象想要学好,那么这个东西你必须要了解,不然后面的原型链,继承,多 ...

  9. 原型 原型链 call / apply

    原型定义: 原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先.通过该构造函数产生的对象,可以继承原型的属性和方法.原型也是对象. 利用原型特点和概念,可以提取共有属性. 对 ...

最新文章

  1. Netflix发布Polly.JS,一个用于HTTP交互的开源库
  2. 【自动驾驶】33.【图像坐标系】 到 【像素坐标系】 的度量单位变换、【英寸】、【感光芯片】
  3. 5行代码满分:L1-053 电子汪 (10分)
  4. 混合多云每个人都应避免的3个陷阱(第2部分)
  5. python 遍历数组根据规律拆分,python 实现以相同规律打乱多组数据
  6. Oracle中查询rownum和rowid的区别
  7. 荣耀x10max能不能升级为鸿蒙,荣耀X10 Max即将亮相:容量很“放肆” 身材却很“克制”...
  8. 隐藏了十年的 Sudo 漏洞曝出:无需密码就能获取 root 权限
  9. mysql+alter+int_MySQL Alter命令
  10. asp.net core 系列 3 依赖注入服务
  11. 表单里面能不能套表单_抽奖+表单,居然还能这么玩?
  12. dw01均衡电路_基于DW01芯片的锂电池保护电路设计
  13. java get与post区别_POST和GET区别
  14. diabetes影响因子2017_DIABETES
  15. 解决vue项目更新版本后浏览器的缓存问题
  16. linux转录组kegg注释,蛋白质组学KEGG注释详解
  17. 什么样的企业适合用EV证书呢?
  18. select update delete
  19. React 项目兼容低版本浏览器
  20. nacos实现服务注册与两种消费方式

热门文章

  1. [Servletamp;JSP] 从JSP到Servlet
  2. PHP —— 识别运算符实现逻辑比较
  3. 请编写一个c程序确定signed,unsigned的char,short,int和long变量取值范围
  4. VS2005 使用过程中有用的一些快捷键和小技巧
  5. 嵌入式 Linux 设备的“瑞士军刀” BusyBox 受多个漏洞影响
  6. 详述近期遭利用的 Atlassian Confluence OGNL 注入漏洞 (CVE-2021-26084)
  7. 微软拒绝修复滥用 MSTSC 的安全绕过缺陷
  8. 苹果新专利曝光,可通过GPS和视觉识别器来识别车辆
  9. 数学之美--关于图论引申出来的爬虫构想
  10. GCD之后台程序运行