js原型和原型链理解到面向对象

一、js中的两种对象,普通对象和函数对象

var obj1 = {}; 
var obj2 =new Object();
var obj3 = new obj1();

function fun1(){}; 
var fun2 = function(){};
var fun3 = new Function('str','console.log(str)');

第一种是普通对象,第二种是函数对象。上面两种对象三种情况,我们首先来分析一下对象,在js中如何实现面向对象思想,所谓的面向对象,对象基于模板来创建,首先定义一个类作为对现实世界的抽象,然后由类来实例化对象;而在原型语言中,对象以克隆另一个对象的方式创建,被克隆的母体称为原型对象。在上述代码中,第一行我们就申明了一个原型对象,也可以理解成面向对象中的“类”,然后我需要干什么?类式抽象的概念,但是我要使用这个类的方法或者属性,那么我们就需要实例化这个类,就是上述代码中的第三行。第二行则式一步完成了申明的同时进行实例化。有实例化对象的概念和“类”(原型对象)的概念接着来分析原型链,初始接触原型链的概念或许有点迷,但是我们要认清原型链,首先得知道他式干什么的,怎么来的。在我个人的理解中,原型链就是实例化对象使用他原型对象的方法属性的一条链子,实例化对象顺着这条链子找方法和属性,这么说或许不太准确,但是却很通俗易懂。说到这里,这条链子怎么找是有他自己的自己的规则的,你要用我就要按照我的规则来式吧,接下来了解一下原型链的规则。

二、创建对象

1、工厂函数模式

function createPerson(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.getName = function () {
return this.name;
}
return o;//使用return返回生成的对象实例
}
var person = createPerson('Jack', 19, 'SoftWare Engineer');

创建对象交给一个工厂方法来实现,可以传递参数,但主要缺点是无法识别对象类型,因为创建对象都是使用Object的原生构造函数来完成的。

2、构造函数模式

function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.getName = function () {
return this.name;
}
}
var person1 = new Person('Jack', 19, 'SoftWare Engineer');

var person2 = new Person('Liye', 23, 'Mechanical Engineer');

这种模式创建对象,Person就是一个“类”的概念,没有事例之前本身只是一个概念,每一次实例化都需要使用new Person()来实例化一个新对象,将Person中的属性和方法赋予实例化对象person2,将函数的作用域赋给实例化对象,this指向实例化对象,并通过实例化过程中传参数,然后返回新对象。

和工厂函数模式不同,没有显式的创建对象,并且直接把属性和方法辅助给this对象。


 这种模式创建对象时,很明显,我们每次实例化一个对象,都会把‘类’对象的上方法和属性全部赋给新对象并进行赋值,函数实例还不是一个作用域中,这样一般是没有问题的,只是会造成内存浪费。比如一个getName,我们并不需要每次都实例化一次,只要使用时能调用到它就可以了,当然我可以将getName函数在Person‘类’对象外定义,但是这样就没有起来封装的效果。

3、原型模式

JS每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,它是所有通过new操作符使用函数创建的实例的原型对象。原型对象最大特点是,所有对象实例共享它所包含的属性和方法,也就是说,所有在原型对象中创建的属性或方法都直接被所有对象实例共享。(注意是共享并不是赋值复制到本身!!!)

function Person(){
}
Person.prototype.name = 'Jack';//使用原型来添加属性
Person.prototype.age = 29;
Person.prototype.getName = function(){
return this.name;
}
var person1 = new Person();
alert(person1.getName());//Jack
var person2 = new Person();
alert(person1.getName === person2.getName);//true;共享一个原型对象的方法

原型是指向原型对象的,这个原型对象与构造函数没有太大关系,唯一的关系是函数的prototype是指向这个原型对象!而基于构造函数创建的对象实例也包含一个内部指针为:[[prototype]]指向原型对象。这就意味着我们在上面的问题解决了,通过“类”对象事例化的新对象使用原型模式继承来的方法,新对象本身不具体这个方法,但是可以顺着这条链子(对象实例也包含一个内部指针为:[[prototype]]指向原型对象)去找“类”对象的原型对象中定义的方法,然后使用他。那么问题又来了,实例化多个方法后,这些所有的实例化对象的在原型对象上的方法和属性都是共享的,那么我们改变了他原型中的方法和属性,大家就会一起改变。无论是在原型对象上直接操作还是实例化对象通过原型“借用”方法和属性时改变,都会改变所有共享者和原型对象本身的方法和属性。

function Person() {
}
Person.prototype.name = 'Jack';
Person.prototype.lessons = ['Math','Physics'];
var person1 = new Person();
person1.lessons.push('Biology');
var person2 = new Person();
alert(person2.lessons);//Math,Physics,Biology,person1修改影响了person2

4、组合构造函数和原型模式

看完上面的同学估计也发现了,构造函数的不足之处被原型模式弥补了,原型模式的不足之恰好我门也可以用构造函数模式来解决,那么我们组合起来使用不就皆大欢喜了。

function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.lessons = ['Math', 'Physics'];
}
Person.prototype = {
constructor: Person,//原型字面量方式会将对象的constructor变为Object,此外强制指回Person
getName: function () {
return this.name;
}
}
var person1 = new Person('Jack', 19, 'SoftWare Engneer');
person1.lessons.push('Biology');
var person2 = new Person('Lily', 39, 'Mechanical Engneer');
alert(person1.lessons);//Math,Physics,Biology
alert(person2.lessons);//Math,Physics
alert(person1.getName === person2.getName);//true,//共享原型中定义方法

上述代码中,我们用构造函数来给每个实例化对象实例化自己一个人使用属性,用原型模式来创建所有实例化对象一起共享的方法。而在我们实际使用中,jq的封装就式采用这种模式的。

5、动态模式

上述组合模式中已经解决了大部分问题,但是上述方法中,构造函数定义属性和原型对象定义共享方法是分离的,和面向对象不太符合。这种情况我们是否想到了我们上门第一个使用的方法工厂函数模型,我们将组合模式封装进工厂函数中。

function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.lessons = ['Math', 'Physics'];
}
if (typeof this.getName != 'function') {//通过判断实例封装
  Person.prototype = {
    constructor: Person,//原型字面量方式会将对象的constructor变为Object,此外强制指回Person
    getName: function () {
      return this.name;
    }
  }
}
var person1 = new Person('Jack', 19, 'SoftWare Engneer');
person1.lessons.push('Biology');
var person2 = new Person('Lily', 39, 'Mechanical Engneer');
alert(person1.lessons);//Math,Physics,Biology
alert(person2.lessons);//Math,Physics
alert(person1.getName === person2.getName);//true,//共享原型中定义方法

 

在这个工厂函数中,所有实例化对象的属性式自己的,使用的原型对象方法会进行一个判断,如果原型对象上有这个方法,那么很好,我们直接用,如果没有呢,那么我们就给原型对象增加一个方法,再使用他。

以上纯属个人学习笔记。

代码和主题学习来自大大 https://www.cnblogs.com/shenjp/p/6517743.html  

 

posted @ 2018-04-10 23:12  Rainy`  阅读(257)  评论(0编辑  收藏  举报