在看jquery源码时遇到一些关于对象的问题,决定再回去学习巩固一下对象的基础。
构造函数
创建构造函数,从而自定义对象类型的属性和方法。构造函数始终都应以一个大写字母开头,该做法借鉴自其它OO语言,为了区别与其他函数。我们使用new操作符,创建一个实例。每创建一个实例,构造函数内的方法就都要在每个实例上重新创建一遍,但俩个方法不是同一个Function实例。
function Person(name) {
this.name = name;
this.sayName = function() {
console.log(this.name);
}
}
var person1 = new Person('aaa');
var person2 = new Person('bbb');
console.log(person1.sayName == person2.sayName);//false
所以我们需要使用原型共享一些属性与方法
原型
原型的共享
javascript中每一个函数都有一个原型属性,prototype就是通过调用构造函数创建的那个对象实例的原型。使用原型对象可以让所有的对象实例共享它所有的属性和方法。
function Person(name) {}
Person.prototype.name = "aaa";
Person.prototype.sayName = function() {
console.log(this.name);
}
var person1 = new Person();
var person2 = new Person();
console.log(person1.sayName == person2.sayName);//true
实例,原型,构造函数三者之间的关系
每个函数都有一个原型属性,这个属性指向函数的原型对象 __proto__存在于实例与构造函数的原型对象之间 原型对象中的constructor属性指向构造函数 构造函数中的this指向实例
new的实现
通过以上的属性,我们可以直接实现new操作符
function Person(name) {
this.name = name;
}
Person.prototype.sayName = function() {
console.log(this.name);
}
var person1 = {};
person1.__proto__ = Person.prototype;
Person.call(person1, "aaa");
person1.sayName();//aaa
实例读取属性与方法的顺序
当代码读取某个对象的某个属性时,从对象本身开始,到构造函数内部,再到原型去获取。相同的属性会屏蔽其他相同的属性,其他相同的属性并不会消失。
function Person() {
this.name = "aaa";
}
Person.prototype.name = "bbb";
var person1 = new Person();
person1.name = "ccc";
console.log(person1.name);//"ccc"
重写原型对象(通过对象字面量来重写整个原型对象)
一、实例未创建前
通过对象字面来重写原型对象会将原来的原型对象覆盖,导致原来的原型对象的constructor指向构造函数被新的原型对象所替换,指向了Object。所以需要在对象字面量中添加constructor属性指向构造函数。
constructor的指向
function abc() {
this.aaa = 1;
}
abc.prototype.bbb = 2;
abc.prototype = {
constructor: abc,
"ccc": 3,
"ddd": 4
}
var a = new abc();
console.log(a.constructor);//abc函数
console.log(abc.prototype.constructor);//abc函数
二、实例创建后
通过对象字面来重写原型对象,该原型对象将不起作用。
重写原型对象代码示例
function abc() {
this.aaa = 1;
}
abc.prototype.bbb = 2;
abc.prototype = {
constructor: abc,
"ccc": 3,
"ddd": 4
}
var a = new abc();
abc.prototype.eee = 5;//创建实例后不是重写,相当于为重写后的原型对象添加新的属性与方法
console.log(a.aaa);//1
console.log(a.bbb);//undefined
console.log(a.ccc);//3
console.log(a.ddd);//4
console.log(a.eee);//5
function abc() {
this.aaa = 1;
}
abc.prototype.bbb = 2;
var a = new abc();
abc.prototype.ccc = 3;//创建实例后不是重写,相当于为原来的原型对象添加新的属性与方法
console.log(a.aaa);//1
console.log(a.bbb);//2
console.log(a.ccc);//3
function abc() {
this.aaa = 1;
}
abc.prototype.bbb = 2;
var a = new abc();
abc.prototype = {//创建实例后重写原型,不起作用
constructor: abc,
"ccc": 3,
"ddd": 4
}
abc.prototype.eee = 5;
console.log(a.aaa);//1
console.log(a.bbb);//2
console.log(a.ccc);//undefined
console.log(a.ddd);//undefined
console.log(a.eee);//undefined
原型对象的问题
function Person() {}
Person.prototype = {
constructor: Person,
arrs: [1, 2]
}
var person1 = new Person();
person1.arrs.push(3);
var person2 = new Person();
console.log(person2.arrs);//[1, 2, 3]
原型对象共享的本质使新创建的person2获取的属性发放所person1影响。
模式
一、组合使用构造函数模式和原型模式 使用最广的模式,结合了构造函数与原型的优缺点。不过多复述。 二、动态原型模式 独立的构造函数和原型,动态原型模式将他们分装在一个构造函数中。 三、寄生构造函数模式 为某一个对象添加额外的方法 四、稳妥构造函数模式 定义私有的变量和属性,只能通过函数内部的方法去获取。
继承
组合继承
function SuperType(name) {
this.name = name;
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name);//继承构造函数内部的属性与方法
this.age = age;
}
SubType.prototype = new SuperType();//创建一个SuperType的实例,将其指向SubType的原型对象
SubType.prototype.constructor = SuperType;
SubType.prototype.sayAge = function() {
console.log(this.age);
}
var pcd = new SubType('pcd', 21);
pcd.sayName();
pcd.sayAge();
注1:新创建的SuperType实例指向SubType的原型对象,其中也包括了SuperType内部的属性与方法,但还是将SuperType的属性与方法继承到SubType中是因为原型对象中方法和属性具有共享性。 注2:SubType的原型不能写成字面量的形式,否则会将继承的原型对象覆盖。
寄生组合继承
function SuperType(name) {
this.name = name;
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name);//继承构造函数内部的属性与方法
this.age = age;
}
SubType.prototype = SuperType.prototype;
SubType.prototype.constructor = SuperType;
SubType.prototype.sayAge = function() {
console.log(this.age);
}
var pcd = new SubType('pcd', 21);
pcd.sayName();
pcd.sayAge();
SuperType的原型对象直接赋值给SubType从而实现继承。
注意事项
function SuperType(name) {
this.name = name;
}
function SubType() {
}
SubType.prototype = SuperType.prototype;
SubType.prototype.constructor = SuperType;
SubType.prototype.sayName = function() {
console.log(this.name);
}
var pcd = new SuperType('pcd');
pcd.sayName();//"pcd"
comments powered by Disqus继承中的父类可以使用子类的原型方法