摘要:子类型最终回报寒潮类型对象的全部实例属性,但是我们不得不在调用子类型构造函数时重写这些属性处理引用类型共用问题组合式继承代码如下第二次调用第一次调用寄生组合式继承代码如下
面向对象语言都有类的概念,但是ECMAScript没有类的概念,所以它的对象与基于类的语言中的对象有所不同。
一、创建对象的几种方式及对比 1.对象字面量与创建Object实例方式var person = { name:"Zhangsan", age:20, gender: "male", sayName: function(){ console.log(this.name); } }
var person = new Object(); person.name = "Zhangsan"; person.age = 20; person.gender = "male"; person.sayName = function(){ console.log(this.name); }
缺点:使用同一个接口创建很多对象,会产生大量的重复代码
2.工厂模式因为ECMAScript没有类的概念,所以用函数来封装创建对象的细节
function createPerson(name,age,gender){ var o = new Object(); o.name = name; o.age = age; o.gender = gender; o.sayName = function(){ console.log(this.name); } return o; } var person1 = createPerson("Zhangsan",20,"male"); var person2 = createPerson("Lisi",24,"male"); console.log(person1 instanceof Object); //true
缺点:工厂模式解决了创建多个相似对象的重复代码问题,但没有解决对象类型识别的问题
3.构造函数模式function Person(name, age, gender){ this.name = name; this.age = age; this.gender = gender; this.sayName = function(){ console.log(this.name); } } var person1 = new Person("Zhangsan",20,"male"); var person2 = new Person("Lisi",24,"male"); console.log(person1 instanceof Person); //true console.log(person1 instanceof Object); //true console.log(person1.constructor == Person); //true console.log(person1.sayName == person2.sayName); //false
以这种方式调用构造函数会经历以下四步:
创建一个新对象
将构造函数的作用域赋给新对象
执行构造函数中的代码
返回新对象
任何函数,只要通过new操作符来调用,那它就可以作为构造函数;如果不通过new操作符来调用,那它就是普通函数
var person = new Person("Zhangsan",20,"male"); person.sayName(); //"Zhangsan" Person("Lisi",24,"male"); window.sayName(); //"Lisi" var o = new Object(); Person.call(o,"Wangwu",22,"female"); o.sayName(); //"Wangwu"
缺点:每个方法都要在每个实例上重新创建一遍,上述例子中的sayName方法就会在每个实例中重新创建一遍
function Person(name, age, gender){ this.name = name; this.age = age; this.gender = gender; this.sayName = sayName; } function sayName(){ console.log(this.name); } var person1 = new Person("Zhangsan",20,"male"); var person2 = new Person("Lisi",24,"male");
上述代码将sayName()函数的定义转移到构造函数外部,这样构造函数中的的sayName是一个指向函数的指针,因此person1、person2就共享了在全局作用域定义的同一个sayName()函数。这么做虽然解决了问题但却带来了新问题:如果对象需要定义很多方法那么就需要定义很多全局函数,那么自定义的引用类型就丝毫没有封装性可言了
4.原型模式 4.1原型对象每个函数都有一个prototype属性,这个属性是一个指针指向一个对象,而这个对象包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处就是让所有对象实例共享它所包含的属性和方法
function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.gender = "male"; Person.prototype.sayName = function(){ console.log(this.name); }; var person1 = new Person(); person1.sayName(); //Nicholas var person2 = new Person(); person2.sayName(); //Nicholas console.log(Person.prototype.isPrototypeOf(person1));//true console.log(Person.prototype.isPrototypeOf(person2));//true console.log(Object.getPrototypeOf(person1) == Person.prototype);//true console.log(Object.getPrototypeOf(person1).name);//Nicholas
当代码读取某个对象的某个属性时,先从对象实例本身开始搜索,如果找到给定名称的属性则返回该属性的值;如果没找到则搜索其指针指向的原型。当为对象实例添加一个属性时,则这个属性就会屏蔽原型对象中保存的同名属性
person1.name = "Zhangsan"; console.log(person1.name); //Zhangsan console.log(person1.hasOwnProperty("name")); //true console.log(person2.hasOwnProperty("name")); //false delete person1.name; console.log(person1.name); //Nicholas4.2原型对象赋值
function Person(){ } Person.prototype = { name:"Nicholas", age:29, gender:"male", sayName:function(){ console.log(this.name); } } var person = new Person(); console.log(person instanceof Object); //true console.log(person instanceof Person); //true console.log(person.constructor == Person); //false console.log(person.constructor == Object); //true Person.prototype.constructor = Person; console.log(person.constructor == Person); //true
使用原型对象赋值操作是会覆盖原型对象中的constructor属性,就会切断原型与构造函数之间的关联
function Person(){ } var person = new Person(); Person.prototype = { name:"Nicholas", age:29, gender:"male", sayName:function(){ console.log(this.name); } } person.sayName(); //error
缺点:由于原型中的所有属性和方法都是共享的,所以对于引用类型属性问题就比较突出
function Person(){ } Person.prototype = { constructor:Person, name:"Nicholas", age:29, gender:"male", love:["swimming","running"], sayName:function(){ console.log(this.name); } } var person1 = new Person(); var person2 = new Person(); person1.love.push("playing games"); console.log(person1.love); //["swimming", "running", "playing games"] console.log(person2.love); //["swimming", "running", "playing games"] console.log(person1.love == person2.love); //true5.组合使用构造函数和原型模式
function Person(name,age,gender){ this.name = name; this.age = age; this.gender = gender; this.love = ["swimming","running"]; } Person.prototype = { constructor:Person, sayName:function(){ console.log(this.name); } } var person1 = new Person("Zhangsan",20,"male"); var person2 = new Person("Lisi",24,"male"); person1.love.push("playing games"); console.log(person1.love); //["swimming", "running", "playing games"] console.log(person2.love); //["swimming", "running"] console.log(person1.love == person2.love); //false
这种模式是使用最广泛、认同度最高的一种创建自定义类型的方法
6.寄生构造函数模式function SpecialArray(name,age,gender){ var array = new Array(); array.push.apply(array,arguments); array.toPipedString = function(){ return this.join("|"); } return array; }
这种模式可以用来为原生引用类型做扩展,寄生构造函数模式返回的对象与构造函数或者与构造函数原型之间没有关系,因此不能依赖instanceof操作符来确定对象类型
二、继承 1.原型链function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function (){ return this.property; } function SubType(){ this.subproperty = false; } SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function (){ return this.subproperty; } var instance = new SubType(); console.log(instance.getSuperValue()); //true
原型链虽然很强大,可以用它来实现继承,但它也存在一些问题,其中,最主要的问题来自包含引用类型值的原型;第二个问题是创建子类型的实例时不能向超类的构造函数中传递参数。
function SuperType(){ this.colors = ["red","yellow","blue"]; } function SubType(){ } SubType.prototype = new SuperType(); var instance1 = new SubType(); instance1.colors.push("green"); console.log(instance1.colors); //["red", "yellow", "blue", "green"] var instance2 = new SubType(); console.log(instance2.colors); //["red", "yellow", "blue", "green"]2.借用构造函数
借用构造函数用于解决原型链中包含引用类型值所带来的问题
function SuperType(){ this.colors = ["red","yellow","blue"]; } function SubType(){ SuperType.call(this); } var instance1 = new SubType(); instance1.colors.push("green"); console.log(instance1.colors); //["red", "yellow", "blue", "green"] var instance2 = new SubType(); console.log(instance2.colors); //["red", "yellow", "blue"]
问题:方法都在构造函数中定义,因此函数复用无从谈起,而且在超类原型中定义的方法对子类而言也是不可见的,结果所有类型都只能使用构造函数模式
3.组合继承function SuperType(name){ this.name = name; this.colors = ["red", "yellow", "blue"]; } SuperType.prototype.sayName = function(){ console.log(this.name); } function SubType(name, age){ SuperType.call(this,name); this.age = age; } SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ console.log(this.age); } var instance1 = new SubType("Zhangsan",20); instance1.colors.push("green"); console.log(instance1.colors); //["red", "yellow", "blue", "green"] instance1.sayName(); //Zhangsan instance1.sayAge(); //20 var instance2 = new SubType("Lisi", 24); console.log(instance2.colors); //["red", "yellow", "blue"] instance2.sayName(); //Lisi instance2.sayAge(); //24
组合继承避免了原型链和借用构造函数的缺陷,融合了他们的优点,成为JavaScript中最常用的继承模式
4.原型式继承var person = { name : "Zhangsan", colors : ["red", "yellow", "blue"] } var anotherPerson = Object.create(person); anotherPerson.name = "Lisi"; anotherPerson.colors.push("green"); var otherPerson = Object.create(person); otherPerson.name = "Wangwu"; otherPerson.colors.push("black"); console.log(person.colors); //["red", "yellow", "blue", "green", "black"] console.log(person.name); //Zhangsan
这种继承方式在想让一个对象与另一个对象保持类似的情况下是完全可以胜任的
5.寄生式继承var person = { name : "Zhangsan", colors : ["red", "yellow", "blue"] } var anotherPerson = Object.create(person); anotherPerson.sayHi = function(){ console.log("hi"); } anotherPerson.sayHi();
使用寄生式继承不能做到函数复用而降低效率
6.寄生组合式继承组合继承最大的问题就在于无论什么情况下都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。子类型最终回报寒潮类型对象的全部实例属性,但是我们不得不在调用子类型构造函数时重写这些属性(处理引用类型共用问题)
组合式继承代码如下:
function SuperType(name){ this.name = name; this.colors = ["red", "yellow", "blue"]; } SuperType.prototype.sayName = function(){ console.log(this.name); } function SubType(name, age){ SuperType.call(this,name); //第二次调用 this.age = age; } SubType.prototype = new SuperType(); //第一次调用 SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ console.log(this.age); }
寄生组合式继承代码如下:
function SuperType(name){ this.name = name; this.colors = ["red", "yellow", "blue"]; } SuperType.prototype.sayName = function(){ console.log(this.name); } function SubType(name, age){ SuperType.call(this,name); this.age = age; } SubType.prototype = Object.create(SuperType.prototype); SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ console.log(this.age); }
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/98460.html
摘要:很多情况下,通常一个人类,即创建了一个具体的对象。对象就是数据,对象本身不包含方法。类是相似对象的描述,称为类的定义,是该类对象的蓝图或原型。在中,对象通过对类的实体化形成的对象。一类的对象抽取出来。注意中,对象一定是通过类的实例化来的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 马上就要到七夕了,离年底老妈老爸...
摘要:很多情况下,通常一个人类,即创建了一个具体的对象。对象就是数据,对象本身不包含方法。类是相似对象的描述,称为类的定义,是该类对象的蓝图或原型。在中,对象通过对类的实体化形成的对象。一类的对象抽取出来。注意中,对象一定是通过类的实例化来的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 马上就要到七夕了,离年底老妈老爸...
摘要:很多情况下,通常一个人类,即创建了一个具体的对象。对象就是数据,对象本身不包含方法。类是相似对象的描述,称为类的定义,是该类对象的蓝图或原型。在中,对象通过对类的实体化形成的对象。一类的对象抽取出来。注意中,对象一定是通过类的实例化来的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 马上就要到七夕了,离年底老妈老爸...
摘要:请记住,这些书中的一些可能不是最新的,但概念和基础仍应适用。是最好的老师之一。的秘密由部分组成。在你完成这些书后,查看书籍和最好的本土书籍。 我看过三本,第1本,第二本,第四本。第一本买的的实体书,其他两本看的是电子书。第一本是大名鼎鼎老道写的,书很薄,但是非常经典。javascirpt忍者秘籍是jquery的作者写的,也是非常经典。you dont kown js系列也是非常好。看了...
摘要:学编程真的不是一件容易的事不管你多喜欢或是多会编程,在学习和解决问题上总会碰到障碍。熟练掌握核心内容,特别是和多线程初步具备面向对象设计和编程的能力掌握基本的优化策略。 学Java编程真的不是一件容易的事,不管你多喜欢或是多会Java编程,在学习和解决问题上总会碰到障碍。工作的时间越久就越能明白这个道理。不过这倒是一个让人进步的机会,因为你要一直不断的学习才能很好的解决你面前的难题...
摘要:函数式编程最后介绍一下函数式编程。函数式编程是一种历史悠久,而又在最近颇为热门的话题。函数式编程在面向对象一词诞生以前就已经存在,不过它在很长一段时间里都被隐藏于过程式编程面向对象也是过程式编程的一种的概念之下。 2.1 JavaScript特点 总结以下几个特点: 解释型语言 类似与C和Java的语法结构 动态语言 基于原型的面向对象 字面量的表现能力 函数式编程 解释型语言:...
阅读 2599·2021-10-14 09:43
阅读 3560·2021-10-13 09:39
阅读 3292·2019-08-30 15:44
阅读 3139·2019-08-29 16:37
阅读 3705·2019-08-29 13:17
阅读 2733·2019-08-26 13:57
阅读 1826·2019-08-26 11:59
阅读 1241·2019-08-26 11:46