摘要:而原型对象都会获得一个构造函数属性,这是一个指向属性所在函数的指针。所以组合使用构造函数模式和原型模式简单来说就是构造函数里面定义实例属性,原型模式定义共享属性。
一、创建对象
创建对象的发展史:
最早
var person = new Object() person.name = "Green"
对象字面量
var person = { name = "Green", age = "25", sayName: function(){ alert("this.name") } }
以上两种都会有大量重复性的代码,于是乎:
工厂模式
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 example = createPerson("Green",25,"无业!");
虽然解决了重复代码问题,但没有解决对象识别(工厂模式无从识别对象的类型,因为全部都是Object,不像Date、Array等,本例中,得到的都是o对象,对象的类型都是Object,因此出现了构造函数模式)
构造函数模式
function Person(name,age,family) { this.name = name; this.age = age; this.family = family; this.say = function(){ alert(this.name); } } var person1 = new Person("lisi",21,["lida","lier","wangwu"]); var person2 = new Person("lisi",21,["lida","lier","lisi"]); /* 这是在创建Person的实例,必须用到new - 创建一个新对象 - 将构造函数的作用域赋过去(this指向新对象) - 执行构造函数的代码 - 返回新对象 */ console.log(person1 instanceof Object); //true console.log(person1 instanceof Person); //true
算是构造函数的特点?
。没有显式的创建对象(拗口)
。将属性方法赋给了this对象
。没有return
instanceof: 识别对象类型
在全局作用域中调用一个函数时,this永远指向window (踩坑了~)
构造函数模式内的方法每次都会在实例上重建一遍,里面的方法在做同一件事,但是实例化后却产生了不同的对象,方法是函数 ,函数也是对象。但如果相同的方法都写在全局作用域里,会产生很多全局函数,失去了这个引用类型的封装性。所以就产生了:
原型模式
function Person() { } Person.prototype.name = "lisi"; Person.prototype.age = 21; Person.prototype.family = ["lida","lier","wangwu"]; Person.prototype.say = function(){ alert(this.name); }; console.log(Person.prototype); //Object{name: "lisi", age: 21, family: Array[3]} var person1 = new Person(); //创建一个实例person1 console.log(person1.name); //lisi var person2 = new Person(); //创建实例person2 person2.name = "wangwu"; person2.family = ["lida","lier","lisi"]; console.log(person2); //Person {name: "wangwu", family: Array[3]} // console.log(person2.prototype.name); //报错 console.log(person2.age); //21
~ 每个函数有一个prototype属性,指向一个对象(该函数的原型对象),用途是包含了一些属性和方法等信息,可以被一些由调用该函数创建的实例所共享。这些信息不必定义在构造函数内,只要添加到原型对象上即可。
~ 而原型对象都会获得一个constructor(构造函数)属性,这是一个指向prototype属性所在函数的指针。(prototype和constructor属性在函数与原型之间互相指)
~ 而创建出的实例内部,又有一个指针,指向原型对象(和构造函数里的prototype指的一样,其实实例与构造函数无关,与他的原型有关),是你吗__proto__?
检测属性
使用 hasOwnProperty() 方法可以检测一个属性是存在于实例还是他的原型中。给定属性存在于实例中会返回true。
in操作符:多带带使用时,无论属性存在于哪里,只要有就是true
for-in循环使用时,返回所有能够通过对象访问的可枚举属性,实例和原型的都包括。入所需要取得对象上所有可枚举的实例属性,推荐Object.key()方法。【深拷贝用过】
原型模式的优点是共享,缺点也是共享(过度)。比如两个实例由调用同一个构造函数得来,其中一个实例修改了原型对象上属性值 ,另一个实例也会共享这个修改。所以:
组合使用构造函数模式和原型模式
简单来说就是构造函数里面定义实例属性,原型模式定义共享属性。
原型链简单描述,就是将一个构造函数的实例赋值给另一个构造函数的原型对象。层层套在一起成为一个链条。是实现继承的方法。
原型链的继承仍然存在共享过度的问题,除此之外子类型实例不能给超类型传递参数。于是我们就要用到:
借用构造函数
基本思想: 在子类型构造函数内部调用超类型构造函数(通过call apply方法)
但是这样方法又必须全定义在构造函数里,又不能复用了。于是就又有了:
组合继承
基本思想:使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承。这样既通过在原型上定义方法实现了函数的复用,又能够保证每个函数都有自己的属性。
function SuperType(name) { this.name = name this.color = ["red", "blue"] } SuperType.prototype.getName = function() { console.log(this.name) } function SubType(name, age) { SuperType.call(this, name) // 继承属性 this.age = age } SubType.prototype.getAge = function() { console.log(this.age) } SubType.prototype = new SuperType() // 继承方法 SubType.prototype.constructor = SubType var instance1 = new SubType("zhangsan", 18) instance1.colors.push("black") console.log(instance1.colors) // "red", "blue", "black" console.log(instance1.getName) // "zhangsan" console.log(instance1.getAge) // 18 var instance2 = new SubType("lisi", 20) console.log(instance2.colors) // "red", "blue" console.log(instance2.getName) // "lisi" console.log(instance2.getAge) // 20属性类型 1、数据属性
有四个描述特性:
configurable 能否删除属性 (*置为false后就无法再改变)
enumerable 能否通过for-in循环返回属性
writable 能否修改属性值
value 从这里读取或者写入属性值
前三项默认值都为true,如果需要修改,需要调用大名鼎鼎的Object.defineProperty()方法
var person = {} Object.defineProperty(person,"name",{ // 三个参数 writable: false, // 这里如果不指定都默认为false value: "Green" }) alert(person.name); // Green person.name = "Blue" alert(person.name); // Green2、访问器属性
包含一对getter(读取访问器属性时调用)和setter(写入访问器属性时调用)函数
有四个描述特性:
configurable 一样
enumerable 一样
get 读取属性时调用的函数
set 写入属性时调用的函数
仍需调用Object.defineProperty()方法来定义
var book = { _year :2004, edition: 1 } Object.defineProperty(book,"year",{ get: function(){ return this._year; } set: function(newValue){ if(newValue > 2004){ this._year = newValue; this.edition += newValue - 2004; } } }) book.year = 2005 alert(book.edition); // 2
这是使用访问器属性的常见方式,即设置一个属性的值会导致其他属性发生变化。
*_year的下划线表示只能通过对象方法访问(不懂,等我再查查)
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/103763.html
摘要:深入之继承的多种方式和优缺点深入系列第十五篇,讲解各种继承方式和优缺点。对于解释型语言例如来说,通过词法分析语法分析语法树,就可以开始解释执行了。 JavaScript深入之继承的多种方式和优缺点 JavaScript深入系列第十五篇,讲解JavaScript各种继承方式和优缺点。 写在前面 本文讲解JavaScript各种继承方式和优缺点。 但是注意: 这篇文章更像是笔记,哎,再让我...
摘要:是完全的面向对象语言,它们通过类的形式组织函数和变量,使之不能脱离对象存在。而在基于原型的面向对象方式中,对象则是依靠构造器利用原型构造出来的。 JavaScript 函数式脚本语言特性以及其看似随意的编写风格,导致长期以来人们对这一门语言的误解,即认为 JavaScript 不是一门面向对象的语言,或者只是部分具备一些面向对象的特征。本文将回归面向对象本意,从对语言感悟的角度阐述为什...
摘要:通常有这两种继承方式接口继承和实现继承。理解继承的工作是通过调用函数实现的,所以是寄生,将继承工作寄托给别人做,自己只是做增强工作。适用基于某个对象或某些信息来创建对象,而不考虑自定义类型和构造函数。 一、继承的概念 继承,是面向对象语言的一个重要概念。通常有这两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。 《JS高程》里提到:由于函数没有签名,...
摘要:一基础接口的意义百度规范扩展回调抽象类的意义我的前端面试经历百度前端掘金博主就读于电子科技大学,大三狗一枚面试是个漫长的过程,从海投到收获电话面试,一面二面三面,一个步骤出错那么后面就宣告终结。 一道常被人轻视的前端 JS 面试题 - 前端 - 掘金 目录前言第一问第二问变量声明提升函数表达式第三问第四问第五问第六问构造函数的返回值第七问最后前言 年前刚刚离职了,分享下我曾经出过的一道...
摘要:创建构造函数后,其原型对象默认只会取得属性至于其他的方法都是从继承来的。上图展示了构造函数的原型对象和现有的两个实例之间的关系。所有原生的引用类型都在其构造函数的原型上定义了方法。 第6章我一共写了3篇总结,下面是相关链接:读《javaScript高级程序设计-第6章》之理解对象读《javaScript高级程序设计-第6章》之继承 工厂模式 所谓的工厂模式就是,把创建具体对象的过程抽象...
阅读 4269·2021-09-26 10:11
阅读 2665·2021-07-28 00:37
阅读 3219·2019-08-29 15:29
阅读 1176·2019-08-29 15:23
阅读 3123·2019-08-26 18:37
阅读 2465·2019-08-26 10:37
阅读 595·2019-08-23 17:04
阅读 2344·2019-08-23 13:44