深入浅出面向对象和原型【概念篇2】

bluesboneW
墙裂建议您在看这篇文章之前阅读一遍我之前写的文章深入浅出面向对象和原型【概念篇1】,因为此篇文章涉及到不少相关概念都在上篇文章里啦

1.1 简单复习对象

对象是老生常谈的概念了,在这里我们来简单复习一下

var obj = {
        a: 1, 
        b: "hello",
        c: true,
        d: function () {
            console.log("hello")
        },
        e: {
            e_1: "对象内储存一个对象"
        }
    }

对象是一个储存一系列无序 key: value【键值对】 的集合的容器

注意:核心是要将对象作为一个容器看待

使用对象作为容器的这个特性我们可以进行封装,这会产生两个好处

  1. 让我们的代码变得优雅、易读
  2. 规避全局变量

1.2 通过构造函数创建对象

1.2.1 关于function的额外知识

  1. function作为构造函数(通过new操作符调用)的时候会 返回 一个类型为function的对象
  2. function可以接受参数,可以根据参数来创建 相同类型不同值 的对象
  3. function实例作用域内有一个constructor属性,这个属性就可以指示其构造器

1.2.2 学会使用 new Function

new 运算符接受一个函数 F 及其参数:new F(arguments...)

  1. 创建类的实例——这步是把一个空的对象的__proto__属性设置为 F.prototype
  2. 初始化实例——函数 F 被传入参数并调用,关键字 this 被设定为该实例
  3. 返回实例
    function People(name) {
        this.name = name
        this.sayName = function () {
            console.log(name)
        }
        // 一般在这个函数里不要 return
        // 如果return引用类型的话,等于把return的值赋值给p1
    }

    People() // this指向全局变量,name和sayName成为了全局变量的属性

    var p1 = new People('sad')
    // 第一步:instance = {} 创建了一个类的实例 —— 空对象,并且将空的对象的__proto__属性设置为 F.prototype,也就是说
    // 第二步:执行函数People(),并传入参数"sad",并将this指向p1
    // 第三步:return instance【实例】 , 即把instance赋值给p1

    // 请注意 People()是一个函数,new People()是构造函数

    var p2 = new People('angry')

1.2.3 instanceof

instance 的中文意思为 实例
那么 instanceof 自然就是用来判断对象是否为某个类型的实例

    console.log(p1 instanceof People) // true
    // 意思是判断 对象(p1)是否为某个类型(People)的实例
    // p1 是由 People 构建出来的,自然是People的实例,返回为true

但需要注意的是,instanceof 运算符的工作原理是检测 类的prototype 是否存在实例的原型链上

    console.log(p1 instanceof Object) // true
    // 因为Object.prototype可以被实例p1以原型链的方式访问到

1.2.4 构造函数存在的问题

构造函数在解决了上面所有问题,同时为实例带来了类型
但可以注意到在上例中,每个的实例的方法作用是一样的
但是每个实例被创建的时候都要重新声明一遍,浪费了内存

    // 我们再来看一下这个函数
    function People(name) {
        this.name = name
        this.sayName = function () {
            console.log(name)
        } // 每个实例的sayName方法却是相同的,而且每出现一个新实例,都是新声明一个函数,大大地浪费了内存
    }

能不能给People的实例都使用同一个sayName方法呢

1.3 构造函数&原型&原型链

1.3.1 构造函数

任何函数使用new表达式就是构造函数,也就是说这个函数成为了一个类

1.3.2 用原型链解决重复创建的问题

  1. 每个对象都会自带一个名称为prototype的属性
  2. prototype属性是一个对象
    // 还是这个例子
    function People(name) {
        this.name = name
        this.sayName = function () {
            console.log(name)
        } 
    }
    var p1 = new People('sad')
    var p2 = new People('angry')
    
    // People本身就有prototype属性
    console.log(People.prototype) // {constructor: ƒ} 

    // 而每个对象也都会带有一个__proto__属性,指向这个构造函数【实例】的类
    // 所有的实例都共用一个prototype
    console.log(p1.__proto__ === People.prototype) // true
    console.log(p2.__proto__ === People.prototype) // true
    // 实例可以通过__prop__访问到其 类 的prototype属性,这就意味着类的prototype对象可以作为一个公共容器,供所有实例访问。
    People.prototype.test = "abc"
    console.log(p1.test) // abc

    // p1这个对象没有test属性,但是可以通过它的__proto__属性访问到People的prototype属性
上述关系我们用一张图来帮助大家更好地理解

clipboard.png

1.3.3 解决内存浪费问题

由上我们可以知道

  1. 所有实例都会通过原型链引用到其类的prototype
  2. prototype相当于所有实例都可以访问到的一个公共容器,这个公共容器也是这些实例的类的属性

    那么如何解决内存浪费问题呢?

    Answer:重复的东西移动到公共容器里就可以了

    function People(name) {
        this.name = name // 每个对象的name属性时不同的,这点无可厚非
    }

    People.prototype.sayName = function () {
        console.log(this.name)
    } // 将sayName方法放入People的原型中

    var p1 = new People('sad')
    var p2 = new People('angry')
    console.log(p1.sayName())
    console.log(p2.sayName())
阅读 754

前端前进指南
Just For Fun

今天,你帮助了社区

754 声望
37 粉丝
0 条评论

今天,你帮助了社区

754 声望
37 粉丝
宣传栏