V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
noahlam
V2EX  ›  JavaScript

JS 中的原型对象

  •  1
     
  •   noahlam · 2018-04-18 09:51:19 +08:00 · 2784 次点击
    这是一个创建于 2410 天前的主题,其中的信息可能已经有所发展或是发生改变。

    JS 中的原型对象

    白天写了一篇 [ JS 中创建对象的方法] ,写完以后感觉意犹未尽(实际情况是感觉原型那块内容没有交 代清楚),所以开这一篇继续聊聊关于 JavaScript 中的原型对象

    相信用过 vue 的童鞋,都经常这样做,用 Vue.prototype.xxx = xxx 把一个方法或者属性添加到 Vue 对象的原型上, 这样,我们在 vue 实例的任何地方,都可以用这个方法或属性了,我最喜欢用的,就是把异步请求库 (我比较喜欢用 axios )挂载到 vue 原型上:

    // 一般是./src/mian.js
    // 这里为了方便理解就直接引入 axios,实际使用,我们可以先用 axios 封装一个异步请求模块
    // 在模块里做一些拦截或者处理,然后再导入这个模块。具体做法看
    import axios from 'axios'
    import Vue from 'vue'
    import App from './App'
    // 为所有 Vue 实例添加一个 post 模块,可以在 vue 实例中直接使用 this.post
    Vue.prototype.post = axios
    new Vue({
      el: '#app',
      components: { App },
      template: '<App/>'
    })
    

    这了是 prototype 就是我们所说的原型,那什么是原型呢? 我们看看 MDN 给的解释

    当谈到继承时,JavaScript 只有一种结构:对象。每个对象都有一个私有属性(称之为 [[Prototype]]), 它指向它的原型对象( prototype )。该 prototype 对象又具有一个自己的 prototype, 层层向上直到一个对象的原型为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

    视乎看起好有点拗口,没关系,我用自己的话总结了一下

    1.prototype 其实就是存在于对象中的一个特殊的对象,你可以把它理解为对象的一个属性或方法, 如 a.prototype,看起来是不是很像对象 a 的一个属性呢?

    2.每个对象都有一个 prototype,除了 null

    那这个 prototype 是干嘛的呢? 其实回头看看上面关于 vue 的代码就知道了, prototype 最主要的作用就是该原型所属对象的所有实例,都能共享 prototype 里的属性和方法 上面的代码中,通过向 Vue.prototype 中添加一个 post 方法,然后就可以在所有 vue 实例中使用该方法,就是个简单的实践。

    我们回头看看 [ JS 中创建对象的方法] 里面的原型模式

    function Student(){}   // 声明一个空函数
    Student.prototype.name = 'xiaohong'
    Student.prototype.age = 17
    Student.prototype.gender = 'f'
    Student.prototype.study = fucntion() { console.log('我在学习...')}
    

    我们先定义了一个空函数,注意:这个时候,我们并没有认为的给函数添加一个 prototype 属性 /方法, 而 Student 却自动有了 prototype,然后我们往 prototype 里面添加了 name,age,gender 属性和 study 方法, 然后我们用 new 实例化 2 个 Student 对象出来

    var studentA = new Student()
    console.log(studentA.name)    // xiaohong
    console.log(studentA.age)     // 17
    console.log(studentA.gender)  // f
    studentA.study()              // 我在学习...
    
    var studentB = new Student()
    console.log(studentB.age)     // xiaohong
    console.log(studentB.name)    // 17
    console.log(studentB.gender)  // f
    studentB.study()              // 我在学习...
    

    上面的例子可以看出,对象的 prototype 里面的属性和方法,在该对象的所有实例里面,都是共享的 那如果我们想要让实例对象有自己的属性 /方法,该怎么办呢? 比如,我想让 studentB 的名字是'lili', 很简单,直接在实例对象上添加该属性 /方法:

    studentA.name = 'lili'
    studentA.study = function () {
        console.log('我在偷懒')
    }
    
    console.log(studentA.name)  // lili
    console.log(studentB.name)  // xiaohong
    studentA.study()            // 我在偷懒
    studentB.study()            // 我在学习...
    

    可以看出,studentA 的属性 /方法被改变的时候,studentB 没有对应的跟着改变,这是为什么呢? 不是说好的全所有 prototype 里的属性 /方法都是共享的吗?事实上,prototype 里的属性 /方法,确实是共享的, 问题出在我们是在实例对象上赋值,所以这个属性 /方法,是属于实例的,而不是属于 prototype 的,prototype 的属性, 也无法在实例对象上写入,也就是说,实例对象和 prototype 上,同时存在了 name 属性和 study 方法,那么, 为什么 studentA 和 studentB 访问到的属性 /方法 会不一样呢? 其实每次访问一个属性 /方法的时候, 都会先从实例对象开始查找,如果实例上有,就直接返回,如果实例上没有,就继续往 prototype 上查找,有就返回, 如果 prototype 上还有 prototype,那么还会继续网上查找,直到原型链的最顶层。如果都没有查到,则会返回 undefined。

    那么新的问题来了,我们该如何判断一个属性,是属于实例本身的,还是属于 prototype 的? 答案是 hasOwnProperty 方法, hasOwnProperty 方法可以检测到实例对象里面有没有给定的属性,该方法只能检测到实例里面的属性,检测不到 prototype 上的

    studentA.hasOwnProperty('name')  // true
    studentB.hasOwnProperty('name')  // false
    

    那如果想同时查找实例对象和原型对象 prototype 呢?我们可以用 in 操作符

    'name' in studentA    // true
    'name' in studentB    // true
    

    有了这 2 个方法,我们就可以组合起来判断属性是属于实例还是原型了。

    这里老是说到实例,不得不提一下,实例对象虽然是构造函数“构造”出来的,但是其实跟构造函数没有直接联系, 实例对象内部指向的是构造函数的 prototype (原型)。 实例跟构造函数的一个间接关系是 实例.prototype.constructor --> 构造函数

    关于原型的介绍就到这里,有需要更深入的童鞋,建议去读一下 javascript 权威指南。里面关于原型的介绍比我这详细。

    下面列几个 javascript 权威指南里面介绍的关于原型的方法

    1. 获取一个实例对象的原型 (ES5 才支持)

      Object.getPrototypeOf(studentA) // Student.prototype

      部分浏览器( chrome,safari,firefox )也支持一个属性 __proto__

      studentA.__proto__ == Student.prototype

    2. 判断一个构造函数是否是指定实例对象的原型

      Student.prototype.isPrototypeOf(studentA) // true

    好了,关于原型对象的话题,就到这里了,感谢收看!

    如果觉得对您有用,请给本文的github加个 star,万分感谢,另外,github 上还有其他一些关于前端的教程和组件,有兴趣的童鞋可以看看,你们的支持就是我最大的动力。

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2654 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 15:01 · PVG 23:01 · LAX 07:01 · JFK 10:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.