构建函数创造自定义类

365 阅读7分钟

构建函数创造自定义类

1. 语法

1.1 ES3语法:

  • 执行语法:
    • new函数()这种方式就是基于构造函数的方式来执行的
  • 约定规范 类名的第一个字母一般都是大写的
  • 特点:
    • 1.Func不在被誉为普通函数,而是叫做构造函数(也就是所谓的自定义类)
    • 2.返回的结果不在是基于RETUTN来判断返回值,返回的结果来当前类的一个实例
  • 想创造自定义类和创造类的实例,只需要在执行的时候,不在“函数()”,普通函数执行;而是“new函数()”执行,也就是构造函数执行,这样方法被称为类,返回结果被称为类的实例;

1.2 构造函数语法与普通函数语法的区别

  • 普通函数
    • ’Fn‘ 是函数本身(不是执行)
    • 'Fn()' 函数执行
  • 构造函数
    • ’new Fn()‘
      • 构造函数执行
      • 有参数new
    • ’new Fn‘
      • 构造函数执行(这种方式不能给函数传递实参了)
      • 无实参new

2. 构造函数执行

  • 构造函数执行(同时拥有普通函数执行的特征,也有自己单独的一些操作)

2.1 执行步骤

    1. 初始化作用域链
    1. 形参赋值
    1. 变量提升
    1. 首先会在当前上下文中,创造一个对象(这个对象就是当前类的实例)
    1. 让当前上下文中的this指向新创建的对象
    1. 代码执行
    1. 代码执行完,如果我们没有设return,浏览器默认会把创造的实例对象返回

2.2.实例的相关问题

    • 构造函数执行,由于具备普通函数特征,所以在私有上下文中可能会出现一些私有变量,但是这些私有变量和实例没有必然的联系
    • 私有上下文中的‘THIS’才是实例,所以只有写‘THIS.XXX=XXX’的操作,才是给实例设置私有属性
    • 实例的私有属性和上下文中的私有变量不是一个东西
    1. 当前类的每一个实例都是单独的一个对象,实例和实例之间是独立的
    1. 在构造函数的函数体中,基于this.xxx=xxx给实例设置的属性和方法都是自己私有的,和其他实例中的属性和方法不冲突

2.3 return相关问题

  • 1.没有return,默认返回当前类的实例对象(对象数据类型)
  • 2.有return,并且返回基本类型值,最后的返回结果还是类的实例,不会有影响
  • 3.如果返回的是一个引用数据类型,则会把默认返回的实例给覆盖掉,这样我们接受结果就不在当前类的实例了,而是自己返回的值
  • 4.真实项目中,如果想创建类的实例,则建议大家不要在手写return了,防止实例被覆盖

3.检测实例的方法

3.1 instaceof

  • 检测当前实例是否属于这个类,或者检测当前值是否为某个类的实例
  • 语法:值instanceof类
  • 返回值:是他的实例返回true,反之返回false
  • 局限:instanceof不适用基本数据类型检测,要求检测的实例必须是对象数据类型的
  • 应用场景:区分对象中的特殊数据格式,例如数组或者正则

3.2 hasOwnProperty

  • 检测当前的某一个属性是否为实例的私有属性
  • 语法:对象.hasOwnProperty(属性)
  • 返回值:是私有属性返回TRUE,不是对象的属性或者不是私有的属性都返回false

3.3 in

  • 检测当前属性是否为对象的属性
  • 语法:属性in对象
  • 返回值:只要是对象的属性(不管是共有还是私有的属性)结果都是TRUE

4.JS创建自定义类型对象的7种方式

4.1 工厂模式

  • 用函数来封装以特定接口创建对象的细节
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;
}

4.2 构造函数模式

  • 自定义构造函数,创建特定类型的对象,同一个构造函数创建出来的对象属于一类。
function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() {
        alert(this.name);
    };

}
var person = new Person("Tom", 25, "Software Engineer");

缺点: 对于一些可以共享且相同的函数方法,却要多次创建在实例化对象中,占用内存,且复用性差。

4.3 原型模式

  • 让所有的对象实例共享原型对象所包含的属性和方法,不必在构造函数中定义然后多次在实例对象中创建了,只需要添加给原型即可。
function Person() {
}

Person.prototype.name = "Tom";
Person.prototype.age = 25;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
    alert(this.name);
};

//当然也可以通过用对象字面量来重写整个原型对象,比上面的更简洁,但不要忘了constructor变化

// Person.prototype = {};

var person1 = new Person();
// 可以指定实例属性,屏蔽来自原型的属性值
person1.name = "Jerry";

缺点: 省略了传递给构造函数初始化参数这一环节,导致所有的实例默认都具有相同的属性值。更大的问题是对于原型上的引用类型属性,所有的实例之间会共享修改,丧失了独特性。

4.4 混合构造函数原型模式

  • 最常见的创建自定义类型方式,构造函数中定义实例属性,原型对象中添加共享属性和方法。
function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelby", "Court"];
}

Person.prototype = {
    constructor : Person,
    sayName : function() {
        alert(this.name);
    }
}

var person1 = new Person("Tom", 25, "Software Engineer");
var person2 = new Person("Jerry", 24, "Software Engineer");

person1.friends.push("Van");
alert(person1.friends); //"Shelby,Count,Van"
alert(person2.friends); //"Shelby,Count"

alert(person1.sayName === person2.sayName); //true

优点: 支持向构造函数传递参数,每个实例都有自己的一份实例属性的副本,每个实例共享对方法的引用,最大限度节省内存

4.5 动态原型模式

  • 将构造函数和原型对象等定义统一到一个函数中,封装性更强,并且通过检测必要情况来决定是否初始化原型,效率更高。
function Person(name, age, job){

    // 属性
    this.name = name;
    this.age = age;
    this.job = job;

    //方法
    if (typeof this.sayName != "function"){
        Person.prototype.sayName = function(){
            alert(this.name);
        };
    }
} 

var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName(); 

评价: 原型方法的添加只执行一次,对原型所做的修改也能立即在实例中反映,可以说相当完美,但是需要注意不能使用对象字面量重写原型,否则会切断现有实例与新原型的联系。

4.6 寄生构造函数模式

  • 当有特殊需求比如说创建一个具有额外方法的数组,由于不能直接修改Array,就可以使用这个模式。
function SpecialArray(){

    //创建数组
    var values = new Array();

    //添加值
    values.push.apply(values, arguments);

    //添加方法
    values.toPipedString = function(){
        return this.join("|");
    };

    //返回数组
    return values;
}

var colors = new SpecialArray("red", "blue", "green");
alert(colors.toPipedString()); //"red|blue|green" 
  • 除了使用了new操作符,并且把使用的包装函数叫做构造函数外,其余和工厂模式一模一样。

  • 注意: 返回的对象与构造函数和原型没有关系,与在构造函数外部创建的对象没有什么不同,因此不能使用instanceof操作符来确定对象类型。

  • 建议在可以使用其他模式的情况下,不要使用这种模式。

4.7 稳妥构造函数模式

  • 用来创建没有公共属性,不引用this的安全稳妥对象。
function Person(name, age, job){

    //创建要返回的对象
    var o = new Object(); 

    //可以在这里定义私有变量和函数

    //添加方法
    o.sayName = function(){
        alert(name);
    };

    //返回对象
    return o;
}

var friend = Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas" 
  • 除了使用sayName方法外,没有其他办法访问name的值。

与寄生构造函数模式类似,使用稳妥构造函数模式创建的对象与构造函数之间没有什么关系,因此instanceof操作符也没有意义。