一、创建对象
创建对象的发展史:
最早
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); // Green
2、访问器属性
包含一对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的下划线表示只能通过对象方法访问(不懂,等我再查查)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。