复习面向对象--创建对象

  最近在看javascript高级程序设计这本书,看到了面向对象这一本部分,感觉很重要,所以再一次复习一遍,总结下知识,篇幅过多,分成了三部分,创建对象,原型和原型链继承,最好可以连着看,不懂得再跳回去看。 

面向对象

  (Object-Oriented,OO)的语言有一个标志,那就是它们都有类的的概念,而通过类可以创建任意个多个具有相同属性和方法的对象。而javascript在es6出来之前没有类的概念。所以javascript的对象和其他语言的对象有很大的不同。

什么是对象:

  简单来说,对象就是没有顺序的并且有属性和对应属性值的集合(对象的每个属性或方法都有名字,而且映射到一种值,这个值可以数据或者函数)。

对象的定义:

  1:字面量:在大括号内部声明属性和对应的属性值,声明方法和其对应的方法名,可多次声明,没有顺序。

var obj = {
    name:'peter',
    age:18,
    sayHi:function(){
        console.log(`Hi`);
    }
};

  name就是属性,'peter'就是对应的属性值。
  由此可见:字面量声明对象只能用于单方面的声明,无法进行复用。最大的缺点就是重复造轮子,产生大量的重复性代码 

  2:工厂模式:使用简单的函数创建对象,为对象添加属性或方法,然后返回这个对象。
  无参数:

function Obj(){
    var obj = new Object();
    obj.name = 'peter';
    obj.age = 18;
    obj.sayHi = function(){
        console.log(`Hi`);
    }
    return obj;
}
var person = Obj();

  很显然:该声明方式很单一,和字面量差不多,都是单一声明,只能适合同一类型同一属性可以重复调用的对象;同一类型的但不同属性的不能调用,只能重新再次声明。

  有参数:

function Obj(name, age){
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.sayHi = function(){
        console.log(`Hi`);
    }
    return obj;
}
var person = Obj('peter', 18);

  工厂模式的传参数声明方式,可以无数次调用该函数来声明某个对象,解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

  3:构造函数模式:通过创建函数,将属性和方法赋给this对象。通过new实例一个对象,进而调用该函数,this指向该对象。

function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayHi = function(){
        console.log(this.name);
    }
}
var peter = new Person('peter',18);
var tom = new Person('tom',22);
  该声明方式是类似与工厂模式,但和工厂模式有所不同,它把属性和方法都定义给了this,每个创建新的实例,都可以使用该构造函数,实现了复用。
  但是构造函数有个缺点,就是其声明的方法本身是个函数,每个实例调用方法都是一样的,所以把方法定义到构造函数外面,作为全局函数,这样实现了方法的复用,但新问题又来了,如果这个实例要调用很多方法,岂不是写很多方法,这样导致的后果就是内存空间占用,浪费资源。

  tip:new操作符进行了哪些步骤:
    1.创建一个新对象,
    2.将构造函数的作用域赋给新对象(this指向新对象)。
    3.执行构造函数的代码(为新对象添加属性或方法)。

  4:原型模式:创建一个构造函数,将属性和方法放到该函数的原型上,实现实例调用其原型上的所有属性和方法。原型和原型链知识可以看这个,传送门

function Person(){
}
Person.prototype.name = 'peter';
Person.prototype.age = 18;
Person.prototype.sayHi = function(){
    console.log(this.name);
}
var peter = new Person();

  每个函数都有prototype属性,这个属性就是一个指针,指向一个对象,这个对象的用途是包含由特定类型的所有实例所共享的属性和方法,使用原型对象就可以让所有实例对象均包含这些属性及方法。
  原型模式声明方式是利用构造函数的壳子,将属性和方法写到其原型上,这样避免了占用内存空间,但是从例子上看,如果多个实例的话,都会指向同一个对象,更改该实例所在的原型对象的值,会直接改变原有的值,如下例子:

Person.prototype.name = 'tom';
Person.prototype.age = 22;

  这样的话缺点很明显,写了实例tom的属性和方法,就改变了实例peter的属性和方法。

  5:混合模式(组合使用构造函数模式和原型模式):利用构造函数写对象的属性,利用原型模式写对象的方法。

function Person(name, age){
    this.name = name;
    this.age = age;
}
Person.prototype.sayHi = function(){
    console.log(`Hi,我叫${this.name}`);
}
var peter = new Person('peter', 18);
peter.sayHi();  // Hi,我叫peter

  混合模式是最常见创建对象的方式,这样的好处是复用了对象的属性,而且方法放到了原型上,不管声明多少个方法,都不占用内存并且互不影响。说缺点吧,emmm,那就是在除js开发人员开发这种模式很另类。。。

  6:动态原型模式:在构造函数模式上,将所有信息都写在构造函数里,包括原型上的方法。

function Person(name,age){
    this.name = name;
    this.age = age;
    if(typeof this.sayHi != 'function'){
        Person.prototype.sayHi = function(){
            console.log(`Hi,我叫${this.name}`);
        }
    }
}

  它把所有信息都封装到了构造函数内,而通过在构造函数中初始化原型,又保持了同时使用构造函数和原型的优点。通过检查某个应该存在的方法是否有效,来决定是否初始化原型。
  大白话就是:如果去掉if的话,每new一次(即每当一个实例对象生产时),都会重新定义一个新的函数,然后挂到Person.prototype.say上,而实际上,只需要定义一次就够了,因为所有的实例都会共享此属性的,所以如果去掉if的话,会造成没有必要的时间和空间的浪费,而加上if后,只在new第一次实例化时会定义say方法,之后都不会再定义了。
  这种方式创建对象是最好又最有效的方式,推荐使用。
  ps!使用动态原型模式,就不能使用字面量重写原型,如果在创建实例的情况下,重写原型,会切断现有实例与新原型的联系。

  7:寄生构造函数模式:在工厂模式(有参数)的基础上,new出一个实例。

function Person(name,age){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayHi = function(){
        console.log(this.name);
    }
    return o;
}
var peter = new Person('peter',18);
peter.sayHi();

  此模式除了new实例外其他的都和工厂模式一样,构造函数在不返回值的情况下,默认会返回新实例,而通过在构造函数的末尾添加一个return语句,可以重写调用构造函数时返回值。
  简单的说,如果没有return,和普通的构造函数一致,如果有return,它就返回想要返回的值。

  ps:构造函数返回的对象和构造函数外部创建的对象没有什么不同,不能依赖instanceof操作符来确定对象的类型。所以!不提倡用该模式。

  8:稳妥构造函数模式:在一些安全的环境中(禁止使用this和new),或者防止数据被其他应用程序改动时使用。在寄生构造函数的基础上,构造函数内部不使用this,外部不使用new.

function Person(name,age){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayHi = function(){
        console.log(name);
    }
    return o;
}
var peter = Person('peter',18);
peter.sayHi(); // peter

  该模式是由道格拉斯.克罗克福德发明的稳妥对象,所谓的稳妥对象就是指的没有公共属性,而且其方法也不应用this的对象。
  在上面的例子上,peter保存的就是一个稳妥对象,除了sayHi方法外,没有别的方式能够访问到其数据成员,虽然外部能够给这个对象添加新的方法或数据成员,但也不可能有别的方法传回到构造函数内的原始数据。
  所以这个模式适合在安全模式下引用。

后记:

  在复习面向对象中,由于篇幅过长,将知识点分成了三部分,面向对象--创建对象,面向对象--原型与原型链面向对象--继承,对应的知识点我放在了github里,有需要的可以去clone学习,觉得好的话,给个star。

  文章有不对或者不理解的地方,请私信或者评论,一起讨论进步。

参考资料:

  javascript高级程序设计(第三版)

原文地址:https://www.cnblogs.com/sqh17/p/9664858.html