JavaScript的学习笔记(day10)

1.工厂模式的概念

  什么是工厂模式:用于创建特定对象时降低代码冗余的一种方式
  正常创建对象的缺点:
    字面量创建对象:一次性,如果需要创建多个对象则会需要写很多重复的代码
    构造函数创建对象:不能看出是一个整体

工厂模式创建对象的过程:

  //将创建对象的代码封装在一个函数中
    function createPerson(name, age, gender) {
      var person = new Object();
      person.name = name;
      person.age = age;
      person.gender = gender;
      person.sayName = function () {
        console.log(this.name);
      }
      return person;
    }
    //利用工厂函数来创建对象
    var person1 = createPerson("zhangsan", 18, 'male');
    var person2 = createPerson("lisi", 20, 'female');

使用工厂模式创建对象的优缺点:
优点:只需要输入参数就可以创建出一个对象
缺点:方法冗余 无法区分种类 因为返回的是一个对象,所以创建的实例的构造函数是Object而不是Person

2.自定义构造函数模式

构造函数:用于创建特定类型对象的方法,例如:Object,Array
自定义构造函数:

    // 自定义构造函数
    function Person(name, age, gender) {
      this.name = name;
      this.age = age;
      this.gender = gender;
      this.sayName = function () {
        console.log(this.name);
      }
    }
    var person1 = new Person('zhangsan', 29, 'male');
    var person2 = new Person('lisi', 19, 'female');

   person1.sayName(); // zhangsan
   person2.sayName(); // lisi
  自定义构造函数与正常的函数创建对象的**区别**:没有显式的创建对象 属性和方法直接赋值给this,
  利用this调用 没有return


  使用自定义构造函数创建一个对象:
    var person1 = new Person('zhangsan', 29, 'male');
    var person2 = new Person('lisi', 19, 'female');

注意:创建的对象会像正常对象一样有一个constructor属性,指向Person。在构造函数内部的this被赋值为这个新对象

  new操作符:
    在使用new操作符创建对象时,new操作符在构造函数中隐式的创建了一个当前构造函数的实例对象,
    修改了this的指向,使其指向实例对象,执行函数体,返回实例对象

  instanceof运算符
    作用:用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上
    语法:console.log(person1 instanceof Object); // true

使用函数表达式的方式声明自定义构造函数:

var Person = function (name, age, gender) {
  this.name = name;
  this.age = age;
  this.gender = gender;
  this.sayName = function () {
    console.log(this.name);
  };
}
var person1 = new Person("zhangsan", 29, "male");
var person2 = new Person("lisi", 27, "female");
person1.sayName(); // zhangsan 
person2.sayName(); // lisi 

注意:在实例化对象时可以不传递参数,而这时就可以不用加构造函数后的括号,只需要new关键字就能调用相应的构造函数

构造函数与普通函数的区别:

构造函数与普通函数没有本质上的区别,构造函数也是一个函数,只是构造函数在调用时是使用new关键字来创建一个对象,而不使用new关键字调用构造函数而是直接调用构造函数,其效果就是一个普通函数

经典案例:

 var Person = function (name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.sayName = function () {
      console.log(this.name);
    };
  }
  // 作为构造函数
  var person = new Person("Jacky", 29, "male");
  person.sayName(); // Jacky
  // 作为函数调用
  Person("lisi", 27, "female"); // 添加到全局对象 node global 浏览器 window
  global.sayName(); // lisi
  // 在另一个对象的作用域中调用
  var o = new Object();
  Person.call(o, "wangwu", 25, "male");
  o.sayName(); // wangwu

注意:在没用使用new关键字调用构造函数时,会将属性和方法添加到全局对象中。
特别注意:当一个函数没有明确设置this的指向时(即没有对象调用,没有使用call,apply,bind改变this指向),this会始终指向global对象(浏览器中指向window对象)
构造函数的问题:在创建对象时,其中的方法都会重新创建一次,即每个通过构造函数创建的对象的方法都是不同的。虽然名字是一样的,但是是不同的两个方法
由于我们没必要定义两个作用一样但是却是两个不同的方法,因此,我们可以把函数的定义转移到构造函数外部

function Person(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.sayName = sayName;
  }
  function sayName() {
    console.log(this.name);
  }
  var person1 = new Person("zhangsan", 29, "male");
  var person2 = new Person("lisi", 27, "female");
  构造函数模式的缺点:由于person1和person2共享了全局作用域上的sayName方法,导致全局作用域会被
  扰乱(即那个函数实际上只能在一个对象上调用。如果这个对象需要多个方法,那么就要在全局作用域中定义
  多个函数)

3.原型模式(用于解决构造函数模式的缺点)

语法:

function Person(){}
Person.prototype.name = "zhangsan";
Person.prototype.age = 29;
Person.prototype.gender = "male";
Person.prototype.sayName = function () {
  console.log(this.name);
};
var person1 = new Person();
person1.sayName(); // zhangsan 
var person2 = new Person();
person2.sayName(); // zhangsan 
console.log(person1.sayName == person2.sayName); // true
  概念:通过每一个函数中都会存在一个prototype属性,包含由特定引用类型的实例属性和方法,并且能够被
  实例对象共享

原型层级:在访问对象属性时,会根据属性名先在对象实例本身开始搜索,如果搜索到了就返回对应的值,如果没有搜索到,则会根据指针向原型对象中搜索直到搜索到最大的引用对象Object,如果还是没有搜索到则返回error。

  根据原型层级的思想可以发现,如果实例对象添加了一个和原型对象中的某一个属性同名的属性,由于首先先会对
  实例对象进行搜索,则会把原型对象上的同名属性遮蔽住(是遮蔽住而不是覆盖,原型对象中的同名属性还继续
  存在,但是访问时会优先返回实例对象上的同名属性值)

注意:即使把实例对象上的同名属性赋值为null也会产生遮蔽问题
如何解决这个遮蔽问题:通过delete操作符完全删除实例对象上的同名属性

原型与in操作符:in操作符可以检测某一个属性是否在实例或原型上
in操作符配合hasOwnPrototype方法判断某一个属性是否是原型属性:
语法:

   function hasPrototypeProperty(object, name) {
      //不在实例中但是可以访问到的属性属于原型属性
      return !object.hasOwnProperty(name) && (name in object);
    }

更简单的原型模式:

function Person() {}
Person.prototype = {
  name: "zhangsan",
  age: 29,
  gender: "male",
  sayName() {
    console.log(this.name);
  }
};
/* 在这个案例中,Person.prototype 被设置为等于一个通过对象字面量创建的
新对象。最终结果是一样的,只有一个问题:这样重写之后,Person.prototype 
的 constructor 属性就不指向 Person了。在创建函数时,也会创建它的 
prototype 对象,同时会自动给这个原型的 constructor 属性赋值。而上面的写
法完全重写了默认的 prototype 对象,因此其 constructor 属性也指向了完全
不同的新对象(Object 构造函数),不再指向原来的构造函数。*/
var person1 = new Person()
console.log(person1.constructor === Person); //false
console.log(person1.constructor === Object); //true

注意:与之前的原型模式相比,这个原型模式更简单,但是由于这种方式相当于重写
了Person.prototype属性,并且并没有重写constructor这个属性,所以当前的
constructor会指向Object而不是Person,需要专门设置这个值。

重新设置constructor的值:
Object.defineProperty(Person.prototype, “constructor”, {
enumerable: false,
value: Person
});

原型模式存在的问题:由于原型模式是把所有的属性和方法维护在原型中的,这可以使得
所有的实例对象都可以贡献这些方法,但是当共享一个引用类型时,相当于是共享同一个内存
地址,这会导致一个实例对象修改了这个引用类型的值,但是另一个实例对象的这个引用类型的
值也会被修改。

4.组合模式(构造函数模式+原型模式)

在组合模式创建实例对象时,对于这个实例对象的私有属性和方法,利用构造函数进行维护,而对于
一些实例对象之间共享的方法和属性,则使用原型模式进行维护。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值