ES5面向对象基础

面向对象的知识时JS中的一个比较重要的概念,我们今天学习一下ES5面向对象的基础内容。

一、 创建对象

  1. 工厂模式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function createPerson(name){
    var o = new Object();
    o.name = name;
    o.sayName = function(){
    console.log(this.name);
    };
    return o;
    }

    var person1 = createPerson("Orange");

缺点:无法辨别是一个对象。

  1. JSON对象
    1
    2
    3
    4
    5
    6
    var person1 = {
    name:"Orange",
    sayName:function (){
    console.log(this.name);
    }
    };

优点:简单。
缺点:每次只能创建一个对象,代码无法复用。

  1. 构造函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function Person(name){
    this.name = name;
    this.sayName = function(){
    console.log(this.name);
    };
    }

    var person1 = new Person("Orange");

    person1.sayName(); //"Orange"

优点:可以创建多个对象,可以辨别是对象。
下面我们主要讨论的也正是使用构造方法创建的对象。

二、 不使用new关键字会发生什么

不使用new的时候相当于时全局变量window调用该方法,this会绑定在全局变量上。由于没有返回值,结果是undefined。

1
2
3
4
5
function Person(name){
this.name = name;// 不使用new时 非严格模式下绑定在全局变量window上了
}
var person1 = Person("Orange");// 方法的调用
console.log(person1);// undefined

三、 new一个对象的时候有返回值怎么办

如果返回的是一个对象,那么new出来的结果就是这个对象;如果返回的是非对象的,那么new返回的与没有return语句一样,是一个该类的实例对象。如果没有new的时候,就比如上面这种情况,那就是方法返回什么结果就是什么了。

1
2
3
4
5
6
7
8
9
10
11
12
function Person1(){
this.name = "Person1";
return {haha:"我是一个新的对象"};// 返回一个对象
}
function Person2(){
this.name = "Person2";
return 123;// 返回一个非对象
}
var person1 = new Person1();
var person2 = new Person2();
console.log(person1);// {haha: "我是一个新的对象"}
console.log(person2);// Person2 {name: "Person2"}

四、 成员方法

  1. 给单个对象添加成员方法

    1
    2
    3
    4
    5
    6
    7
    8
    function Person(){
    }

    var person1 = new Person();
    person1.name = "Orange";
    var person2 = new Person();
    console.log(person1.name);// "Orange"
    console.log(person2.name);// undefined
  2. 给多个对象添加成员方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function Person(name){
    this.name = name;//所有的对象公用的属性
    this.age = 20;//所有的对象公用的属性
    }

    var person1 = new Person("Orange");
    var person2 = new Person("小明");
    person1.age = 21;
    console.log(person1.name);// "Orange"
    console.log(person2.name);// "小明"
    console.log(person1.age);// 21
    console.log(person2.age);// 20

五、 私有变量

构造函数中var声明的变量是私有变量。

1
2
3
4
5
6
function Person(){
var age = 21;//这里相当于私有变量
}

var person1 = new Person();
console.log(person1.age);// undefined

六、 静态方法

添加到构造函数上的方法是静态方法,添加到构造函数上的变量是静态变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Person(name){
this.name = name;
}

Person.age = 20;//静态变量
Person.name = "骗你的"; // 这条语句会忽略 因为name是一个特殊的属性 不能修改
Person.sayName = function (){//静态方法
console.log("sayName:" + this.name);
};
var person = new Person("Orange");
console.log(person.name);// "Orange"
console.log(Person.name);// "Person" 这里是类的名称 哈哈
// console.log(person.name);// 这个会报错 静态属性属于类 而不是属于对象
// person.sayName(); // 这个会报错 静态方法属于类 而不是属于对象
console.log(Person.age); // 20
Person.sayName();// sayName:Person

七、 原型

上面说过给多个对象添加成员方法,只要在构造函数中添加this.方法名 = 方法即可,但是这样会出现一个问题就是不同对象的方法是不同的,这样就会增大内存的开销。解决这个问题的办法就是把方法放在函数的外面,然后在函数内部去引用同一个函数。这样解决了这个问题,但是却失去了封装性。解决这个问题的最终办法就是——原型。

每一构造函数写完以后,引擎会加原型的委托,就比如上面的Person类(函数)定义完了以后,引擎会加:

1
Person.prototype = new Object();

从上面代码可以看出Person加了一个名叫prototype的静态变量,这个变量就是Person的原型。由于对象是无法访问静态变量的,所以浏览器给每一个对象又加了一个__proto__的属性也指向了这个原型。相当于引擎又执行了下面的代码:

1
person.__proto__ = Person.prototype;

我们这里总结一下:对象.__proto__属性构造函数.prototype属性 都指向了该类的原型
下面我们把这剪不断理还乱的原型画一张图吧:
prototype示意图

由上图可知,原型对象有一个constructor的属性指向了构造方法。也就是:

1
2
Person.prototype.constructor === Person;// true
person.__proto__.constructor === Person;// true

由上面Person.prototype = new Object();可知Person.prototype其实是Object的对象,既然是对象那么就有__proto__属性指向该对象的原型。也就是说:

1
Person.prototype.__proto__ === Object.prototype;// true

其实Object.prototype也是一个对象那么他的原型是什么呢?我们可以打印一下发现他的原型是null,上图也可以看出。

1
console.log(Object.prototype.__proto__);// null

说了这么一大堆终于知道了原型是什么了,那么还有一个更重要的东西就是原型链。
如果一个对象调用自己没有定义的属性,那么他就会从他们原型中查找,看有没有定义该属性,如果原型中没有就会去原型的原型去寻找,一直找到顶层的Object类的对象(最顶层的null不用找了,肯定没有),这条由原型连起来的链条叫做原型链。

那我们看一个东西,上面person.constructor是什么呢?其实就是Person函数。因为person对象没有constructor属性,所以去person的原型中找也就是person.__proto__而它里面找到了,指向的就是Person函数。

我们也可以用原型来定义对象的属性和方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Person(){
}

Person.prototype.name = "Orange";
Person.prototype.sayName = function(){
console.log(this.name);
};

var person1 = new Person();
var person2 = new Person();

person2.name = "小明";
person1.sayName();// "Orange"
person2.sayName();// "小明"

我们来看一下这个步骤,person1调用sayName的时候由于person1自己没有sayName这个方法,那么就是去它的原型去找也就是Person.prototype,发现有这个方法,那么就调用,而方法中this.name,由于自己并没有name这样一个属性,也会在原型中找,最后找到了是”Orange”。person2sayName跟之前的是一样的,但是person2name这个属性,所以就不需要去原型链中找了,该属性的值是”小明”。

-------------本文结束 感谢您的阅读-------------
0%