1

一、创建对象

创建对象的发展史:

最早

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的下划线表示只能通过对象方法访问(不懂,等我再查查)


绿绿
33 声望3 粉丝