创建对象---笔记【js高级程序设计】

几个关于类的前提概念:

  • 类/继承描述了一种代码的组织结构形式,类意味着复制
  • 类的核心概念:类、继承、实例化和多态【多态可以简述为父类的通用行为被子类用更特殊的行为重写】;
  • javascript不存在类,但基于类是一种设计模式这一前提,js通过一些方法近似实现了类的功能;

1.工厂模式

// 工厂模式
    function person(name, age, title) {
      var o = {}
      o.name = name;
      o.age = age;
      o.title = title;
      o.saySomething = function() {
        console.log(this.name);
      }
      return o
    }
    
    var person1 = person('孙悟空', 18, '齐天大圣')
    var person2 = person('猪八戒', 26, '天蓬元帅')

    console.log(person1);
    console.log(person2);
打印结果
{name: "孙悟空", age: 18, title: "齐天大圣", saySomething: ƒ}
age: 18
name: "孙悟空"
saySomething: ƒ ()
title: "齐天大圣"
__proto__:
constructor: ƒ Object() 
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
问题
  • 创建的对象之间没有任何联系,缺乏可识别的类似类的特性;

2.构造函数

// 构造函数模式
    function Person(name, age, title) {
      this.name = name;
      this.age = age;
      this.title = title;
      this.saySomething = function() {
        console.log(this.name);
      }
    }
    
    var person1 =new Person('孙悟空', 18, '齐天大圣')
    var person2 =new Person('猪八戒', 26, '天蓬元帅')

    console.log(person1);
    console.log(person2);

打印结果

Person {name: "孙悟空", age: 18, title: "齐天大圣", saySomething: ƒ}
age: 18
name: "孙悟空"
saySomething: ƒ ()
title: "齐天大圣"
__proto__:
   constructor: ƒ Person(name, age, title)
   __proto__:
      constructor: ƒ Object()
      hasOwnProperty: ƒ hasOwnProperty()
      isPrototypeOf: ƒ isPrototypeOf()
      propertyIsEnumerable: ƒ propertyIsEnumerable()
      toLocaleString: ƒ toLocaleString()
      toString: ƒ toString()
      valueOf: ƒ valueOf()
      __defineGetter__: ƒ __defineGetter__()
      __defineSetter__: ƒ __defineSetter__()
      __lookupGetter__: ƒ __lookupGetter__()
      __lookupSetter__: ƒ __lookupSetter__()
      get __proto__: ƒ __proto__()
      set __proto__: ƒ __proto__()

特征

  • 没有显式的创建对象
  • 属性和方法直接赋值给this对象
  • 没有return语句
  • __ proto __属性中保存着constructor【构造函数】属性,该属性指向Person

问题:

  • 方法的重新创建会造成内存的浪费
  • 将方法定义在全局,然后将引用赋值给this.saySomething,可以解决重复创建多个相同函数的问题,但又会引入方法缺乏封装性的问题

3.原型模式

函数的prototype属性:

打印构造函数Person的结果

向构造函数的原型上添加属性和方法:

function Person() { 
    }
    // 向构造函数的原型上添加属性和方法
      Person.prototype.name = '孙悟空';
      Person.prototype.age = 18;
      Person.prototype.title = '齐天大圣';
      Person.prototype.saySomething = function() {
        console.log(this.name);
      }

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

    console.log(person1);
    console.log(person2);
    console.dir(Person);

实例对象和构造函数的打印结果

__ proto __和prototype的解析:

Snipaste_2020-09-12_15-53-29.png

4.原型模式语法的精简

function Person() {}
    // 向构造函数的原型上添加属性和方法
    Person.prototype = {
      name: '孙悟空',
      age: 18,
      title: '齐天大圣',
      saySomething: function () {
        console.log(this.name);
      }
    }
    
    var person1 = new Person()
    var person2 = new Person()

    console.log(person1);
    console.log(person2);
    console.dir(Person);
打印结果

这里对prototype赋值对象,完全重写了prototype,通过以下代码保证constructor的指向;

 Person.prototype = {
      // 保证contructor的指向
      constructor: Person,
      name: '孙悟空',
      age: 18,
      title: '齐天大圣',
      saySomething: function () {
        console.log(this.name);
      }
    }

注意: 此时的constructor的可遍历性为真,原生的是不可遍历的,可通过Object.defineProperty()进行数据属性的设置;

感觉挺重要的3点:

  • 实例与原型之间的连接只不过是一个指针,而非是一个副本;
  • 原型具有动态性,即可以先创建实例,之后再在原型上添加新属性或方法,实例依然能够读取原型上的属性或方法;
  • 重写原型会丧失这种动态性

原型对象的问题:

function Person() {}
    // 向构造函数的原型上添加属性和方法
    Person.prototype = {
      // 保证contructor的指向
      constructor: Person,
      name: '孙悟空',
      age: 18,
      title: '齐天大圣',
      friends: ['八戒', '沙僧'],
      saySomething: function () {
        console.log(this.name);
      }
    }
    

    var person1 = new Person()
    var person2 = new Person()
//修改其中一个实例person1中的引用类型的属性值
    person1.friends.push('哪吒')
    console.log(person1);
    console.log(person2);
    console.dir(Person);
打印结果

注:基本属性的值会产生屏蔽,即在实例上修改同名属性,会在实例上创建属性,并且通过实例访问时,会遮蔽原型上的同名属性;

5.构造函数和原型的结合

简述就是用构造函数来定义实例属性【保证实例属性的独立性】,原型模式用于定义方法和共享属性

  function Person(name, age, title) {
      this.name = name;
      this.age = age;
      this.title = title;
      // 这样实例的引用类型的属性就是独立的了
      this.friends = ['八戒', '沙僧']
      this.saySomething = function () {
        console.log(this.name);
      }
    }

    Person.prototype = {
      constructor: Person,
      saySomething: function () {
        console.log(this.name);
      }
    }

    var person1 = new Person('孙悟空', 18, '齐天大圣')
    var person2 = new Person('猪八戒', 26, '天蓬元帅')

    console.log(person1);
    console.log(person2);
    console.dir(Person);
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容