5.1 寄生构造函数模式
function Person(name) { var o = new Object(); o.name = name; o.getName = function () { console.log(this.name); }; return o; } var person1 = new Person('kevin'); console.log(person1 instanceof Person) // false console.log(person1 instanceof Object) // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function Person(name) {
var o = new Object();
o.name = name;
o.getName = function () {
console.log(this.name);
};
return o;
}
var person1 = new Person('kevin');
console.log(person1 instanceof Person) // false
console.log(person1 instanceof Object) // true
|
寄生构造函数模式,我个人认为应该这样读:
寄生-构造函数-模式,也就是说寄生在构造函数的一种方法。
也就是说打着构造函数的幌子挂羊头卖狗肉,你看创建的实例使用 instanceof 都无法指向构造函数!
这样方法可以在特殊情况下使用。比如我们想创建一个具有额外方法的特殊数组,但是又不想直接修改Array构造函数,我们可以这样写:
function SpecialArray() { var values = new Array(); for (var i = 0, len = arguments.length; i len; i++) { values.push(arguments[i]); } values.toPipedString = function () { return this.join("|"); }; return values; } var colors = new SpecialArray('red', 'blue', 'green'); var colors2 = SpecialArray('red2', 'blue2', 'green2'); console.log(colors); console.log(colors.toPipedString()); // red|blue|green console.log(colors2); console.log(colors2.toPipedString()); // red2|blue2|green2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
function SpecialArray() {
var values = new Array();
for (var i = 0, len = arguments.length; i len; i++) {
values.push(arguments[i]);
}
values.toPipedString = function () {
return this.join("|");
};
return values;
}
var colors = new SpecialArray('red', 'blue', 'green');
var colors2 = SpecialArray('red2', 'blue2', 'green2');
console.log(colors);
console.log(colors.toPipedString()); // red|blue|green
console.log(colors2);
console.log(colors2.toPipedString()); // red2|blue2|green2
|
你会发现,其实所谓的寄生构造函数模式就是比工厂模式在创建对象的时候,多使用了一个new,实际上两者的结果是一样的。
但是作者可能是希望能像使用普通 Array 一样使用 SpecialArray,虽然把 SpecialArray 当成函数也一样能用,但是这并不是作者的本意,也变得不优雅。
在可以使用其他模式的情况下,不要使用这种模式。
但是值得一提的是,上面例子中的循环:
for (var i = 0, len = arguments.length; i len; i++) { values.push(arguments[i]); }
1
2
3
|
for (var i = 0, len = arguments.length; i len; i++) {
values.push(arguments[i]);
}
|
可以替换成:
values.push.apply(values, arguments);
1
|
values.push.apply(values, arguments);
|
新创建的实例方法不引用 this
深入系列
JavaScript深入系列目录地址:。
JavaScript深入系列预计写十五篇左右,旨在帮大家捋顺JavaScript底层知识,重点讲解如原型、作用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难点概念。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎star,对作者也是一种鼓励。
- JavaScirpt 深入之从原型到原型链
- JavaScript 深入之词法作用域和动态作用域
- JavaScript 深入之执行上下文栈
- JavaScript 深入之变量对象
- JavaScript 深入之作用域链
- JavaScript 深入之从 ECMAScript 规范解读 this
- JavaScript 深入之执行上下文
- JavaScript 深入之闭包
- JavaScript 深入之参数按值传递
- JavaScript 深入之call和apply的模拟实现
- JavaScript 深入之bind的模拟实现
- JavaScript 深入之new的模拟实现
- JavaScript 深入之类数组对象与 arguments
JavaScript 深入之创建对象的多种方式以及优缺点
1 赞 3 收藏 评论
JavaScript创建对象方式总结
- object构造函数、对象字面量
//object构造函数
// 优点:简单方便
// 缺点:批量创建对象很麻烦,不能使用instanceof来确定对象类型
var person = new Object();
person.name = "masike";
person.age=19;
person.job="student";
person.sayName=function(){
console.log(this.name);
};
//字面量
var person = {
name:"masike",
age:22,
job:"student",
sayName:function(){
console.log(this.name);
}
}```
- 工厂模式:简单的函数创建对象,为对象添加属性和方法,然后返回对象,这个模式后来被构造函数所取代。
```JavaScript
//工厂模式
// 优点:减少了代码量
// 缺点:未能解决对象识别问题
function createPerson(name,age,job){
var o=new Object();
o.name=name;
o.age=19;
o.job="student";
o.sayName=function(){
console.log(this.name);
}
return o;
}
var person1=createPerson("masike",19,"student");
var person2=createPerson("withershins",20,"worker");```
- 构造函数模式:自定义引用类型,像创建对象实例一样使用new操作符,缺点是每个成员无法得到复用,包括函数。
//构造函数模式
//优点:在工厂模式的基础下解决了对象识别问题
//缺点:每个实例的方法都是独立的,多数情况下同个对象的实例方法都是一样的
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
console.log(this.name);
}
}
var person1=new Person("masike",19,"student");
var person2=new Person("withershins",19,"worker");
//偏方
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=sayName;
}
function sayName(){
console.log(this.name);
}
var person1=new Person("masike",19,"student");
var person2=new Person("withershins",19,"worker");```
- 原型模式:使用prototype属性共享属性和方法。
//原型模式
//优点:公用原型减少了赘余
//缺点:在原型的改变会影响到所有的实例,于是实例没有了独立性
function Person(){
}
Person.prototype.name="masike";
Person.prototype.age=19;
Person.prototype.job="student";
Person.prototype.sayName=function(){
console.log(this.name);
}
var person1=new Person();
person1.sayName();
var person2=new Person();
person2.sayName();
console.log(person1.sayName==person2.sayName);```
- 组合使用构造函数模式和原型模式:构造函数定义实例属性,原型定义共享的属性和方法。
//组合使用构造函数和原型模式
//优点:结合了构造函数和原型模式的优点,并解决了缺点
//缺点:代码没有被很好地封装起来
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=student;
this.friends=["num1","num2"];
}
Person.prototype={
constructor:Person,
sayName:function(){
console.log(this.name);
}
}
var person1=new Person("masike",19,"student");
var person2=new Person("withershins",19,"worker");
person1.friends.push("vash");
console.log(person1.friends);
console.log(person2.friends);
console.log(person1.friends===person2.friends);
console.log(person1.sayName===person2.sayName);```
1. 工厂模式
function createPerson(name) { var o = new Object(); o.name = name; o.getName = function () { console.log(this.name); }; return o; } var person1 = createPerson('kevin');
1
2
3
4
5
6
7
8
9
10
11
|
function createPerson(name) {
var o = new Object();
o.name = name;
o.getName = function () {
console.log(this.name);
};
return o;
}
var person1 = createPerson('kevin');
|
缺点:对象无法识别,因为所有的实例都指向一个原型
}
写在前面
本文讲解JavaScript各种继承方式和优缺点。
但是注意:
这篇文章更像是笔记,哎,再让我感叹一句:《JavaScript高级程序设计》写得真是太好了!
JavaScript继承方式总结
继承:超类构造函数中有属性有方法,超类原型中有属性有方法,子类想要继承超类的构造函数,超类原型中的部分属性和方法,于是便有了继承。
- 原型链继承:讲一个类型的实例赋值给另一个构造函数的原型,子类型就能够访问超类型所有的属性和方法
//原型链的继承
//缺点:对象实例共享所有的属性和方法,因此不适合单独使用。
function Parent(){
this.name="mike";
}
function Child(){
this.age=19;
}
Child.prototype=new Parent();//子类原型等于父类实例
var test =new Child();
console.log(test.age);
console.log(test.name);
function Brother(){
this.weight=60;
}
Brother.prototype=new Child();
var brother=new Brother();
console.log(brother.name);
console.log(brother.age);```
- 借用构造函数模式
//借用构造函数/类式继承call()/apply()
//可以传递参数,但是方法无法共享
function Parent(age){
this.name=['mike','jack','smith'];
this.age=age;
}
function Child(age){
Parent.call(this,age);
}
var test=new Child(21);
console.log(test.age);//21
console.log(test.name);//mike,jack,smith
test.name.push('bill');
console.log(test.name);//mike,jack,smith,bill
//call()和apply()用法区别
The difference is that apply lets you invoke the function with arguments
as an array;
call requires the parameters be listed explicitly.
A useful mnemonic is "A for array and C for comma(逗号)."```
- 组合式继承:原型链和构造函数结合的方法,原型链继承共享的属性和方法,构造函数继承实例属性。
//组合式继承
//组合构造函数和原型链
//原型链继承原型属性和方法,构造函数实现实例属性的继承
function Parent(name){
this.name=name;
this.arr=['aaa','bbb','ccc'];
}
Parent.prototype.run=function(){
return this.name;
};
function Child(name,age){
Parent.call(this,age);//第二次调用
this.age=age;
}
Child.prototype=new Parent();//第一次调用```
- 原型式继承:不必预先定义构造函数的情况下实现继承,本质是执行给定对象的浅复制,而复制的副本还可以得到进一步的改造。
//借助于原型并基于已有的对象创建新对象,同时还不用创建自定义类型
function obj(o){
function F(){}
F.prototype=o;
return new F();
}
var box={
name:"masike",
arr:['baba','mama','didi']
};
var b1=obj(box);
console.log(b1.name);//masike
b1.name='mike';
console.log(b1,name);//mike
console.log(b1,arr);//baba,mama,didi
b1.arr.push("parents");
console.log(b1.arr);//baba,mama,didi,parents
var b2=obj(box);
console.log(b2.name);//masike
console.log(b2.arr);//baba,mama.didi,parents
- 寄生式继承:基于某个对象后某些信息创建一个对象,然后增强对象,最后返回对象。
function create(o){
var f=obj(o);
f.run=function(){
return this.arr;
}
return f;
}```
- 寄生组合式继承:集寄生式继承和组合是继承优点于一身,是实现基于类型继承的最有效的方式。解决组合继承模式由于多次调用父类构造函数而导致低效率问题。
//寄生组合式类型
//解决了父类构造函数两次调用问题
function obj(o){ //(原型式)
function F(){}
F.prototype=o;
return new F();
}
function create(parent,test){
var f=obj(parent.prototype);//创建对象
f.constructor=test;//增强对象
}
function Parent(name){
this.name=name;
this.arr=['brother','sister','parents'];
}
Parent.prototype.run=function(){
return this.name;
}
function Child(name,age){
Parent.call(this,name);
this.age=age;
}
Child.prototype = obj(Parent.prototype);//实现继承
var test=new Child("masike",19);
test.arr.push("withershins");
console.log(test.arr);
console.log(test.run());//只共享了方法
var test2=new Child("jack",22);
console.log(test2.arr);//引用问题解决```
未完待续......
>继承最推荐的解决方案:
if(!Object.create){//object.create()是ES5新增方法
Object.create= (function(){
function F(){} //创建中介函数(bridge)
return function(obj) {
if(arguments.length !== 1) {
throw new Error("仅支持一个参数");
}
F.prototype = obj; //原形绑定
return new F(); //返回实例
}
})()
//最终返回的结果,既是F的实例属性,享有F构造函数中的所有属性和方法(因为F构造函数为空,所以完全不用担心会有多余不想要的属性方法存在),[[prototype]]又指向F.prototype,返回的结果是一个对象!!!
}
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.walk = function() {//写到了prototype中,walk一定是想要共享的方法
console.log("走路....");
}
function Child(name, age, address) {
Person.call(this, name, age);//这里继承了person构造函数中想要传递的一些属性
this.address = address;
}
Child.prototype = Object.create(Person.prototype);//不要再使用new了!
Child.prototype.talk = function() {
console.log("说话ing.....")
}
//不用new的原因是因为你不想要Child继承Person构造函数中的所有属性和方法,而是想让他单独继承Person.prototype中共享的属性和方法。```
JavaScript 深入之创建对象的多种方式以及优缺点
2017/05/28 · JavaScript · 对象
原文出处: 冴羽
console.log(this.name);
4.原型式继承
function createObj(o) { function F(){} F.prototype = o; return new F(); }
1
2
3
4
5
|
function createObj(o) {
function F(){}
F.prototype = o;
return new F();
}
|
就是 ES5 Object.create 的模拟实现,将传入的对象作为创建的对象的原型。
缺点:
包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样。
var person = { name: 'kevin', friends: ['daisy', 'kelly'] } var person1 = createObj(person); var person2 = createObj(person); person1.name = 'person1'; console.log(person2.name); // kevin person1.firends.push('taylor'); console.log(person2.friends); // ["daisy", "kelly", "taylor"]
1
2
3
4
5
6
7
8
9
10
11
12
13
|
var person = {
name: 'kevin',
friends: ['daisy', 'kelly']
}
var person1 = createObj(person);
var person2 = createObj(person);
person1.name = 'person1';
console.log(person2.name); // kevin
person1.firends.push('taylor');
console.log(person2.friends); // ["daisy", "kelly", "taylor"]
|
注意:修改person1.name
的值,person2.name
的值并未发生改变,并不是因为person1
和person2
有独立的
name 值,而是因为person1.name = 'person1'
,给person1
添加了 name
值,并非修改了原型上的 name 值。
JavaScript创建对象方法总结精彩博文
javascript继承讲解精彩博文
于江水
继承讲解
2.1 构造函数模式优化
function Person(name) { this.name = name; this.getName = getName; } function getName() { console.log(this.name); } var person1 = new Person('kevin');
1
2
3
4
5
6
7
8
9
10
|
function Person(name) {
this.name = name;
this.getName = getName;
}
function getName() {
console.log(this.name);
}
var person1 = new Person('kevin');
|
优点:解决了每个方法都要被重新创建的问题
缺点:这叫啥封装……
person2.getName(); // daisy
JavaScript 深入之继承的多种方式和优缺点
2017/05/28 · JavaScript · 继承
原文出处: 冴羽
写在前面
这篇文章讲解创建对象的各种方式,以及优缺点。
但是注意:
这篇文章更像是笔记,因为《JavaScript高级程序设计》写得真是太好了!
this.name = name;
2.借用构造函数(经典继承)
function Parent () { this.names = ['kevin', 'daisy']; } function Child () { Parent.call(this); } var child1 = new Child(); child1.names.push('yayu'); console.log(child1.names); // ["kevin", "daisy", "yayu"] var child2 = new Child(); console.log(child2.names); // ["kevin", "daisy"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function Parent () {
this.names = ['kevin', 'daisy'];
}
function Child () {
Parent.call(this);
}
var child1 = new Child();
child1.names.push('yayu');
console.log(child1.names); // ["kevin", "daisy", "yayu"]
var child2 = new Child();
console.log(child2.names); // ["kevin", "daisy"]
|
优点:
1.避免了引用类型的属性被所有实例共享
2.可以在 Child 中向 Parent 传参
举个例子:
function Parent (name) { this.name = name; } function Child (name) { Parent.call(this, name); } var child1 = new Child('kevin'); console.log(child1.name); // kevin var child2 = new Child('daisy'); console.log(child2.name); // daisy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function Parent (name) {
this.name = name;
}
function Child (name) {
Parent.call(this, name);
}
var child1 = new Child('kevin');
console.log(child1.name); // kevin
var child2 = new Child('daisy');
console.log(child2.name); // daisy
|
缺点:
方法都在构造函数中定义,每次创建实例都会创建一遍方法。
5.2 稳妥构造函数模式
function person(name){ var o = new Object(); o.sayName = function(){ console.log(name); }; return o; } var person1 = person('kevin'); person1.sayName(); // kevin person1.name = "daisy"; person1.sayName(); // kevin console.log(person1.name); // daisy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function person(name){
var o = new Object();
o.sayName = function(){
console.log(name);
};
return o;
}
var person1 = person('kevin');
person1.sayName(); // kevin
person1.name = "daisy";
person1.sayName(); // kevin
console.log(person1.name); // daisy
|
所谓稳妥对象,指的是没有公共属性,而且其方法也不引用 this 的对象。
与寄生构造函数模式有两点不同:
- 新创建的实例方法不引用 this
- 不使用 new 操作符调用构造函数
稳妥对象最适合在一些安全的环境中。
稳妥构造函数模式也跟工厂模式一样,无法识别对象所属类型。
- 组合模式
5. 寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
function createObj (o) { var clone = object.create(o); clone.sayName = function () { console.log('hi'); } return clone; }
1
2
3
4
5
6
7
|
function createObj (o) {
var clone = object.create(o);
clone.sayName = function () {
console.log('hi');
}
return clone;
}
|
缺点:跟借用构造函数模式一样,每次创建对象都会创建一遍方法。
4.1 动态原型模式
function Person(name) { this.name = name; if (typeof this.getName != "function") { Person.prototype.getName = function () { console.log(this.name); } } } var person1 = new Person();
1
2
3
4
5
6
7
8
9
10
|
function Person(name) {
this.name = name;
if (typeof this.getName != "function") {
Person.prototype.getName = function () {
console.log(this.name);
}
}
}
var person1 = new Person();
|
注意:使用动态原型模式时,不能用对象字面量重写原型
解释下为什么:
function Person(name) { this.name = name; if (typeof this.getName != "function") { Person.prototype = { constructor: Person, getName: function () { console.log(this.name); } } } } var person1 = new Person('kevin'); var person2 = new Person('daisy'); // 报错 并没有该方法 person1.getName(); // 注释掉上面的代码,这句是可以执行的。 person2.getName();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
function Person(name) {
this.name = name;
if (typeof this.getName != "function") {
Person.prototype = {
constructor: Person,
getName: function () {
console.log(this.name);
}
}
}
}
var person1 = new Person('kevin');
var person2 = new Person('daisy');
// 报错 并没有该方法
person1.getName();
// 注释掉上面的代码,这句是可以执行的。
person2.getName();
|
为了解释这个问题,假设开始执行var person1 = new Person('kevin')
。
如果对 new 和 apply 的底层执行过程不是很熟悉,可以阅读底部相关链接中的文章。
我们回顾下 new 的实现步骤:
- 首先新建一个对象
- 然后将对象的原型指向 Person.prototype
- 然后 Person.apply(obj)
- 返回这个对象
注意这个时候,回顾下 apply 的实现步骤,会执行 obj.Person 方法,这个时候就会执行 if 语句里的内容,注意构造函数的 prototype 属性指向了实例的原型,使用字面量方式直接覆盖 Person.prototype,并不会更改实例的原型的值,person1 依然是指向了以前的原型,而不是 Person.prototype。而之前的原型是没有 getName 方法的,所以就报错了!
如果你就是想用字面量方式写代码,可以尝试下这种:
function Person(name) { this.name = name; if (typeof this.getName != "function") { Person.prototype = { constructor: Person, getName: function () { console.log(this.name); } } return new Person(name); } } var person1 = new Person('kevin'); var person2 = new Person('daisy'); person1.getName(); // kevin person2.getName(); // daisy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
function Person(name) {
this.name = name;
if (typeof this.getName != "function") {
Person.prototype = {
constructor: Person,
getName: function () {
console.log(this.name);
}
}
return new Person(name);
}
}
var person1 = new Person('kevin');
var person2 = new Person('daisy');
person1.getName(); // kevin
person2.getName(); // daisy
|
与寄生构造函数模式有两点不同:
1.原型链继承
function Parent () { this.name = 'kevin'; } Parent.prototype.getName = function () { console.log(this.name); } function Child () { } Child.prototype = new Parent(); var child1 = new Child(); console.log(child1.getName()) // kevin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function Parent () {
this.name = 'kevin';
}
Parent.prototype.getName = function () {
console.log(this.name);
}
function Child () {
}
Child.prototype = new Parent();
var child1 = new Child();
console.log(child1.getName()) // kevin
|
问题:
1.引用类型的属性被所有实例共享,举个例子:
function Parent () { this.names = ['kevin', 'daisy']; } function Child () { } Child.prototype = new Parent(); var child1 = new Child(); child1.names.push('yayu'); console.log(child1.names); // ["kevin", "daisy", "yayu"] var child2 = new Child(); console.log(child2.names); // ["kevin", "daisy", "yayu"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
function Parent () {
this.names = ['kevin', 'daisy'];
}
function Child () {
}
Child.prototype = new Parent();
var child1 = new Child();
child1.names.push('yayu');
console.log(child1.names); // ["kevin", "daisy", "yayu"]
var child2 = new Child();
console.log(child2.names); // ["kevin", "daisy", "yayu"]
|
2.在创建 Child 的实例时,不能向Parent传参
深入系列
JavaScript深入系列目录地址:。
JavaScript深入系列预计写十五篇左右,旨在帮大家捋顺JavaScript底层知识,重点讲解如原型、作用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难点概念。
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎star,对作者也是一种鼓励。
- JavaScirpt 深入之从原型到原型链
- JavaScript 深入之词法作用域和动态作用域
- JavaScript 深入之执行上下文栈
- JavaScript 深入之变量对象
- JavaScript 深入之作用域链
- JavaScript 深入之从 ECMAScript 规范解读 this
- JavaScript 深入之执行上下文
- JavaScript 深入之闭包
- JavaScript 深入之参数按值传递
- JavaScript 深入之call和apply的模拟实现
- JavaScript 深入之bind的模拟实现
- JavaScript 深入之new的模拟实现
JavaScript 深入之类数组对象与 arguments
1 赞 收藏 评论
};
6. 寄生组合式继承
为了方便大家阅读,在这里重复一下组合继承的代码:
function Parent (name) { this.name = name; this.colors = ['red', 'blue', 'green']; } Parent.prototype.getName = function () { console.log(this.name) } function Child (name, age) { Parent.call(this, name); this.age = age; } Child.prototype = new Parent(); var child1 = new Child('kevin', '18'); console.log(child1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
function Parent (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = new Parent();
var child1 = new Child('kevin', '18');
console.log(child1)
|
组合继承最大的缺点是会调用两次父构造函数。
一次是设置子类型实例的原型的时候:
Child.prototype = new Parent();
1
|
Child.prototype = new Parent();
|
一次在创建子类型实例的时候:
var child1 = new Child('kevin', '18');
1
|
var child1 = new Child('kevin', '18');
|
回想下 new 的模拟实现,其实在这句中,我们会执行:
Parent.call(this, name);
1
|
Parent.call(this, name);
|
在这里,我们又会调用了一次 Parent 构造函数。
所以,在这个例子中,如果我们打印 child1 对象,我们会发现 Child.prototype
和 child1 都有一个属性为colors
,属性值为['red', 'blue', 'green']
。
那么我们该如何精益求精,避免这一次重复调用呢?
如果我们不使用 Child.prototype = new Parent() ,而是间接的让 Child.prototype 访问到 Parent.prototype 呢?
看看如何实现:
function Parent (name) { this.name = name; this.colors = ['red', 'blue', 'green']; } Parent.prototype.getName = function () { console.log(this.name) } function Child (name, age) { Parent.call(this, name); this.age = age; } // 关键的三步 var F = function () {}; F.prototype = Parent.prototype; Child.prototype = new F(); var child1 = new Child('kevin', '18'); console.log(child1);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
function Parent (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name, age) {
Parent.call(this, name);
this.age = age;
}
// 关键的三步
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
var child1 = new Child('kevin', '18');
console.log(child1);
|
最后我们封装一下这个继承方法:
function object(o) { function F() {} F.prototype = o; return new F(); } function prototype(child, parent) { var prototype = object(parent.prototype); prototype.constructor = child; child.prototype = prototype; } // 当我们使用的时候: prototype(Child, Parent);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
function prototype(child, parent) {
var prototype = object(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}
// 当我们使用的时候:
prototype(Child, Parent);
|
引用《JavaScript高级程序设计》中对寄生组合式继承的夸赞就是:
这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。
2. 构造函数模式
function Person(name) { this.name = name; this.getName = function () { console.log(this.name); }; } var person1 = new Person('kevin');
1
2
3
4
5
6
7
8
|
function Person(name) {
this.name = name;
this.getName = function () {
console.log(this.name);
};
}
var person1 = new Person('kevin');
|
优点:实例可以识别为一个特定的类型
缺点:每次创建实例时,每个方法都要被创建一次
但是值得一提的是,上面例子中的循环:
3.组合继承
原型链继承和经典继承双剑合璧。
function Parent (name) { this.name = name; this.colors = ['red', 'blue', 'green']; } Parent.prototype.getName = function () { console.log(this.name) } function Child (name, age) { Parent.call(this, name); this.age = age; } Child.prototype = new Parent(); var child1 = new Child('kevin', '18'); child1.colors.push('black'); console.log(child1.name); // kevin console.log(child1.age); // 18 console.log(child1.colors); // ["red", "blue", "green", "black"] var child2 = new Child('daisy', '20'); console.log(child2.name); // daisy console.log(child2.age); // 20 console.log(child2.colors); // ["red", "blue", "green"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
function Parent (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = new Parent();
var child1 = new Child('kevin', '18');
child1.colors.push('black');
console.log(child1.name); // kevin
console.log(child1.age); // 18
console.log(child1.colors); // ["red", "blue", "green", "black"]
var child2 = new Child('daisy', '20');
console.log(child2.name); // daisy
console.log(child2.age); // 20
console.log(child2.colors); // ["red", "blue", "green"]
|
优点:融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式。
3.2 原型模式优化
function Person(name) { } Person.prototype = { constructor: Person, name: 'kevin', getName: function () { console.log(this.name); } }; var person1 = new Person();
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function Person(name) {
}
Person.prototype = {
constructor: Person,
name: 'kevin',
getName: function () {
console.log(this.name);
}
};
var person1 = new Person();
|
优点:实例可以通过constructor属性找到所属构造函数
缺点:原型模式该有的缺点还是有
var person1 = new Person('kevin');
4. 组合模式
构造函数模式与原型模式双剑合璧。
function Person(name) { this.name = name; } Person.prototype = { constructor: Person, getName: function () { console.log(this.name); } }; var person1 = new Person();
1
2
3
4
5
6
7
8
9
10
11
12
|
function Person(name) {
this.name = name;
}
Person.prototype = {
constructor: Person,
getName: function () {
console.log(this.name);
}
};
var person1 = new Person();
|
优点:该共享的共享,该私有的私有,使用最广泛的方式
缺点:有的人就是希望全部都写在一起,即更好的封装性
values.push(arguments[i]);
3.1 原型模式优化
function Person(name) { } Person.prototype = { name: 'kevin', getName: function () { console.log(this.name); } }; var person1 = new Person();
1
2
3
4
5
6
7
8
9
10
11
12
|
function Person(name) {
}
Person.prototype = {
name: 'kevin',
getName: function () {
console.log(this.name);
}
};
var person1 = new Person();
|
优点:封装性好了一点
缺点:重写了原型,丢失了constructor属性
这样方法可以在特殊情况下使用。比如我们想创建一个具有额外方法的特殊数组,但是又不想直接修改Array构造函数,我们可以这样写:
3. 原型模式
function Person(name) { } Person.prototype.name = 'keivn'; Person.prototype.getName = function () { console.log(this.name); }; var person1 = new Person();
1
2
3
4
5
6
7
8
9
10
|
function Person(name) {
}
Person.prototype.name = 'keivn';
Person.prototype.getName = function () {
console.log(this.name);
};
var person1 = new Person();
|
优点:方法不会重新创建
缺点:1. 所有的属性和方法都共享 2. 不能初始化参数
不使用 new 操作符调用构造函数
};
稳妥对象最适合在一些安全的环境中。
};
}
但是作者可能是希望能像使用普通 Array 一样使用 SpecialArray,虽然把 SpecialArray 当成函数也一样能用,但是这并不是作者的本意,也变得不优雅。
o.getName = function () {
Person.prototype.name = 'keivn';
function SpecialArray() {
寄生-构造函数-模式,也就是说寄生在构造函数的一种方法。
}
寄生构造函数模式,我个人认为应该这样读:
var person1 = createPerson('kevin');
Person.prototype = {
优点:实例可以通过constructor属性找到所属构造函数
}
2.1 构造函数模式优化
var person1 = new Person();
注意这个时候,回顾下 apply 的实现步骤,会执行 obj.Person 方法,这个时候就会执行 if 语句里的内容,注意构造函数的 prototype 属性指向了实例的原型,使用字面量方式直接覆盖 Person.prototype,并不会更改实例的原型的值,person1 依然是指向了以前的原型,而不是 Person.prototype。而之前的原型是没有 getName 方法的,所以就报错了!
var person1 = new Person('kevin');
console.log(this.name);
如果你就是想用字面量方式写代码,可以尝试下这种:
this.getName = function () {
var colors = new SpecialArray('red', 'blue', 'green');
name: 'kevin',
console.log(person1.name); // daisy
o.name = name;
优点:该共享的共享,该私有的私有,使用最广泛的方式
values.toPipedString = function () {
constructor: Person,
3.2 原型模式优化
};
}
this.getName = getName;
function Person(name) {
来自《JavaScript高级程序设计》
return o;
if (typeof this.getName != "function") {
function createPerson(name) {
o.getName = function () {
缺点:重写了原型,丢失了constructor属性
console.log(this.name);
function Person(name) {
}
getName: function () {
function Person(name) {
}
getName: function () {
Person.prototype = {
o.name = name;
};
person1.sayName(); // kevin
person1.getName(); // kevin
缺点:对象无法识别,因为所有的实例都指向一个原型
}
values.push(arguments[i]);
}
person1.name = "daisy";
}
return o;
};
console.log(this.name);
person1.sayName(); // kevin
}
console.log(colors2.toPipedString()); // red2|blue2|green2
function person(name){
稳妥构造函数模式也跟工厂模式一样,无法识别对象所属类型。
优点:解决了每个方法都要被重新创建的问题
console.log(this.name);
constructor: Person,
如果对 new 和 apply 的底层执行过程不是很熟悉,可以阅读底部相关链接中的文章。
console.log(this.name);
console.log(this.name);
你会发现,其实所谓的寄生构造函数模式就是比工厂模式在创建对象的时候,多使用了一个new,实际上两者的结果是一样的。
// 注释掉上面的代码,这句是可以执行的。
首先新建一个对象
getName: function () {
缺点:原型模式该有的缺点还是有
var person1 = new Person();
优点:实例可以识别为一个特定的类型
}
person2.getName();
console.log(person1 instanceof Object) // true
5.2 稳妥构造函数模式
然后将对象的原型指向 Person.prototype
console.log(name);
5.1 寄生构造函数模式
this.name = name;
return this.join("|");
return values;
解释下为什么:
console.log(colors2);
function Person(name) {
console.log(this.name);
function Person(name) {
- 构造函数模式
var person2 = new Person('daisy');
缺点:有的人就是希望全部都写在一起,即更好的封装性
// 报错 并没有该方法
var colors2 = SpecialArray('red2', 'blue2', 'green2');
}
Person.prototype = {
var values = new Array();
if (typeof this.getName != "function") {
function Person(name) {
getName: function () {
}
可以替换成:
console.log(colors);
};
优点:封装性好了一点
优点:方法不会重新创建
所谓稳妥对象,指的是没有公共属性,而且其方法也不引用 this 的对象。
}
function Person(name) {
Person.prototype = {
}
var person1 = person('kevin');
}
function Person(name) {
function Person(name) {
for (var i = 0, len = arguments.length; i < len; i++) {
然后 Person.apply(obj)
console.log(colors.toPipedString()); // red|blue|green
3.1 原型模式优化
我们回顾下 new 的实现步骤:
if (typeof this.getName != "function") {
constructor: Person,
也就是说打着构造函数的幌子挂羊头卖狗肉,你看创建的实例使用 instanceof 都无法指向构造函数!
this.name = name;
console.log(this.name);
var o = new Object();
}
var person2 = new Person('daisy');
}
缺点:这叫啥封装……
在可以使用其他模式的情况下,不要使用这种模式。
var o = new Object();
缺点:1. 所有的属性和方法都共享 2. 不能初始化参数
}
构造函数模式与原型模式双剑合璧。
返回这个对象
}
function Person(name) {
var person1 = new Person();
Person.prototype.getName = function () {
person1.getName();
}
Person.prototype = {
this.name = name;
console.log(this.name);
Person.prototype.getName = function () {
var o = new Object();
console.log(this.name);
4.1 动态原型模式
}
return o;
为了解释这个问题,假设开始执行var person1 = new Person('kevin')。
values.push.apply(values, arguments);
this.name = name;
function getName() {
name: 'kevin',
- 工厂模式
};
var person1 = new Person('kevin');
getName: function () {
return new Person(name);
}
this.name = name;
var person1 = new Person();
console.log(person1 instanceof Person) // false
}
var person1 = new Person();
for (var i = 0, len = arguments.length; i < len; i++) {
}
var person1 = new Person();
constructor: Person,
- 原型模式
o.sayName = function(){
缺点:每次创建实例时,每个方法都要被创建一次
var person1 = new Person('kevin');
注意:使用动态原型模式时,不能用对象字面量重写原型
var person1 = new Person('kevin');
}
};
本文由澳门金莎娱乐网站发布于澳门金莎娱乐网站,转载请注明出处:【澳门金莎娱乐网站】JavaScript深入之创建对象的
关键词: