创建对象
以下是几种创建对象的方法
工厂模式
1 2 3 4 5 6 7 8 9
| function createPerson(name, age) { var o = new Object({}); o.name = name; o.age = age; o.sayName = function() { console.log(o.name); }; return o; }
|
缺点是:无法识别对象
构造函数模式
1 2 3 4 5 6 7 8 9 10 11 12
| function Person(name, age) { this.name = name; this.age = age;
this.sayName = function() { console.log(this.name); }; } var persion1 = new Person("lycheng", 123); console.log(person1.constructor == Person); console.log(person1 instanceof Person); console.log(person1 instanceof Object);
|
上述代码中,如果不用 new
1 2 3
| var person2 = Person("person2", 123); console.log(person2); global.sayName();
|
因为当在全局环境中调用函数,this
总是指向 global
,所以在使用构造函数的时候必须使用 new
来新建对象。
构造函数模式比起工厂模式更加像面向对象了,但是有个问题就是内部的函数都是每个对象不同的。意味着在新建对象的时候又要重新新建一个方法对象。
原型模式
1 2 3 4 5 6 7 8 9 10 11 12 13
| function Person() { }
Person.prototype.name = "lycheng"; Person.prototype.age = 25; Person.prototype.sayName = function() { console.log(this,name); };
var person1 = new Person(); var person2 = new Person();
console.log(person1.sayName == person2.sayName);
|
从上面的代码可见,对象 person1 和 person2 的 sayName 函数是同一个了。也可以才用下方的字面值的写法
1 2 3 4 5 6 7 8 9 10 11 12
| function Person() { }
Person.prototype = { constructor: Person, name: "lycheng", skills: ["javascript", "python"], age: 25, sayName: function() { console.log(this.age); } };
|
简单的原型模式大概如此,这种方法的问题在于,没有构造函数,所有的实例共享所有的变量,如果是引用类型的化,则会出现问题。
1 2 3 4 5 6
| var person1 = new Person(); person1.sayName(); person1.skills.push("linux");
var person2 = new Person(); console.log(person2.skills);
|
组合使用构造函数和原型
所以常用的会有混合使用构造函数和原型对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function Person(name, age) { this.name = name; this.age = age; this.skills = []; }
Person.prototype = { sayName: function() { console.log(this.name); } };
var person1 = new Person("lycheng", 25); person1.skills.push("linux"); console.log(person1.skills);
var person2 = new Person("lyc", 18); person2.skills.push("vim"); console.log(person2.skills);
|
这种是比较常用的方法,还有更多的动态原型模式,寄生构造函数模式就不一一赘述了。
继承
原型链
在之前的文章里也提到过原型,javascript 实现面向对象主要通过原型来实现。通过使用原型,可以实现实例对象共有的方法。这种原型对象的关系称为原型链。
每个对象都有原型,通过指针可以查看原型对象,直到最后为 null。通过这样的方法,如果一个对象的原型指向另一个对象,就可以通过这样的方式实现继承。
继承
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 Person(name) { this.name = name; }
Person.prototype.sayName = function(){ console.log(this.name); };
function Student(name, age) { Person.call(this, name, age); this.age = age; }
Student.prototype = new Person(); Student.prototype.constructor = Student;
Student.prototype.sayAge = function() { console.log(this.age); };
var student = new Student("lycheng", 25); student.sayName(); student.sayAge();
|
上述代码中主要通过构造函数来继承实例属性,通过原型链来实现原型方法和属性的继承。
Object.create
ES5 中可以使用新的方法实现继承,将上述的代码改成如下即可
1
| Student.prototype = Object.create(Person.prototype);
|
简单来讲
new X is Object.create(X.prototype) with additionally running the constructor function.
Object.create
执行构造函数,但其实我们这里并不需要构造函数,我们仅仅需要的是它的原型部分。但这里两者起到的作用是一样的。
个人想法
javascript 一些面向对象的实现看起来很诡异,感觉像是硬要为了实现面向对象而将这一套方法论套在基于原型的 javascript 里面。
参考
- Javascript 高级程序设计
- 继承与原型链
- javascript 面向对象介绍
- create