资讯专栏INFORMATION COLUMN

javascript基础-Vue数据绑定前奏对象属性

zebrayoung / 2397人阅读

摘要:关于有人说我用删除这个属性不就好了之后打印发现它还是一只哈士奇。如下的解释如下操作符会从某个对象上移除指定属性。

javascript-Object-Property

? javascript-对象的属性的延伸学习

前言

在学习vue数据绑定的较底层原理时,被setter和getter困惑了很久,一路追根溯源,通过阅读《你不知道的javascript》和红宝书理解了迷惑我的setter、getter。

首先了解什么是属性描述符

[](http://xurenjie.cn:3000/img/d...
在ES5之前,javascript语言没有提供可以检验属性特性的方法,是否只读?不知道;是否可配置?不知道;是否能用for in枚举?不知道。

ES5之后就有了如下的属性描述符:

 var Dogger = {
    breed: "柴犬"
 }

 Object.getOwnPropertyDescriptor( Dogger , "breed" );

输出:

 {
     value: "柴犬",
     writable: true,
     configurable: true,
     enumerable: true
 }

 

在这,创建了一个品种为柴犬的dogger。[[Value]]特性将设置为"柴犬",之后操作对breed的任何修改将反映到这个位置。对,没错,这个getOwnPropertyDescriptor( Dogger , "breed" )函数就是我们要的检验属性特性的方法。

默认:在创建普通对象属性时,属性描述符会使用默认值,即可写可配可枚举。(都为true)

下面分别介绍一下、这几个属性

writeable

决定是否修改属性的值,是否可以指定新的值给它

    Dogger.breed = "哈士奇"

很好理解,当writeable为false的时候,其实定义了一个空的setter(等会会提),这个操作将无效,在严格模式下会抛出一个TypeError的错误。

configurable

与configurable紧密相连的就是defineProperty( )这个方法了,当configurable: false 将不可使用‘好基友’defineProperty( )来配置。后面还会介绍一个会受影响的delete

var Dogger = {
    breed: "柴犬"
 }

Object.defineProperty( Dogger, "breed", {
    value: "哈士奇",
    writable: true,
    configurable: false,
    enumerable: true
} )

Dogger.breed // "哈士奇" | 哈哈!我变成了一只哈士奇
Object.defineProperty( Dogger, "breed", {
    value: "柴犬",
    writable: true,
    configurable: true,
    enumerable: true
} ) // TypeError

这只作死的柴犬在通过defineProperty( )把自己配置成哈士奇之后,顺便把configurable修改为false,这样之后defineProperty( )不管是否严格模式都将报TypeError的错误,这是单向操作,无法撤销。 一失足成千古恨~
[](http://xurenjie.cn:3000/img/d...

例外

还是可以通过writable的方式修改breed的嘛~,不过这里有一个方法可以让dogger彻底绝望,使breed无法修改,也就是这个例外:这个时候defineProperty( )还是可以使用的(如下),只可以修改writable,configurable需要与刚才的false一致。

Object.defineProperty( Dogger, "breed", {
    value: "哈士奇",
    writable: false,
    configurable: false,
    enumerable: true
} )

这样之后柴犬永远变成了只哈士奇。
[](http://xurenjie.cn:3000/img/d...

关于delete

有人说我用delete删除这个breed属性不就好了?

delete Dogger.breed

之后打印dogger发现它还是一只哈士奇。如下:

MDN的解释如下

delete 操作符会从某个对象上移除指定属性。成功删除的时候回返回 true,否则返回 false。
Non-configurable properties cannot be removed. This includes properties ofbuilt-in objects like Math, Array, Object and properties that are created as non-configurable with methods like Object.defineProperty( ).
When in strict mode, if delete is used on a direct reference to a variable, a function argument or a function name, it will throw a SyntaxError.
Any variable defined with var is marked as non-configurable. In the following example, salary is non-configurable and cannot be deleted. In non-strict mode, the delete operation will return false.

delete只是用来直接删除对象(可删除的)属性,当breed属性是Dogger的最后引用者,对这个属性执行delete操作,这个为引用的对象就可以被垃圾回收了,不要看成一个释放内存的工具,而是删除属性的操作,仅此。

enumerable

当且仅当该属性的 enumerable 为 true 时,该属性才能够出现在对象的枚举属性中。默认为 false。
属性特性 enumerable 定义了对象的属性是否可以在 for...in 循环和 Object.keys( ) 中被枚举。

放上MDN的代码片段:

var o = {};
Object.defineProperty(o, "a", { value : 1, enumerable:true });
Object.defineProperty(o, "b", { value : 2, enumerable:false });
Object.defineProperty(o, "c", { value : 3 }); // enumerable defaults to false
o.d = 4; // 如果使用直接赋值的方式创建对象的属性,则这个属性的enumerable为true

for (var i in o) {
console.log(i);
}
// 打印 "a" 和 "d" (in undefined order)

Object.keys(o); // ["a", "d"]

o.propertyIsEnumerable("a"); // true
o.propertyIsEnumerable("b"); // false
o.propertyIsEnumerable("c"); // false

[ [ Get ] ]和[ [ Put ] ] [ [ Get ] ]
Dogger.breed

如上,对一个对象进行访问时有一个很重要的细节。Dogger.breed是一次属性访问,但并不是仅仅在Dogger中查找breed,其实看起来更像是在语言规范中执行了Dogger的[ [ Get ] ]操作,看上去像[ [ Get ] ]( )。

在对象中查找有没有这个属性。

在该对象的原型链上查找有没有这个属性。

都没找到返回undefined(注意:如果那个属性值恰好为undefined时,虽然返回值一样,但是底层发生的事是不一样的

[ [ Put ] ]

[ [ Get ] ]对应[ [ Put ] ]操作,一旦给对象属性赋值就触发设置和创建这个属性发生的事情是这样的:

首先确定是否存在这个属性。breed是否存在

存在,是否是setter。是setter就调用setter,是否是setter来给breed赋值

writable是否为false,false则无效。breed的writable是否为false

设置值为该属性的值

Getter和Setter

在《 javascript高级程序设计 》中成为访问器属性,也称为访问描述符,getter和setter是两个隐形的函数,getter为读取属性值的函数,setter为设置属性值的函数,在访问这个阶段我们关注的是四个属性:

set

get

configurable

enumerable

这时候我们用Dogger的例子来了解一下这些特性

var Dogger = {
    get breed() {
        return  "柴犬"
    }
}
Object.defineProperty(
  Dogger
  "breed-type",
  {
    get: function() {
      return this.breed + "品种"
    }
  }
)
Dogger.breed // "柴犬"
Dogger.breed-type // "柴犬品种"

没毛病,不管是隐式调用还是显式确实能够让我们定义属性,自动调用隐藏函数,返回值为属性访问的返回值
[](http://xurenjie.cn:3000/img/d...
如果这时候,我们想用赋值操作给Dogger改变属性会怎么样?

Dogger.breed = "哈士奇"

Dogger.breed // "柴犬"

由于只定义了breed的getter,所以对它的值进行设置时set操作会忽略赋值操作(也不会报错)。其实就算定义了setter,自定义
的getter还是只会返回getter设置的值。

因此你去改变属性的值时,你还需要定义一个setter,通常来说,他们是成双成对的。不写严格模式会报错。

setter其实就是我们最常用的赋值操作

var Dogger = {
    get breed() {
        return  "柴犬"
    }
    set breed(val) {
        this._breed_ = val
    }
}

Dogger.breed = "哈士奇"
Dogger.breed // "哈士奇"

这样一来,赋值操作就可以改变啦!我们把赋值操作存储给新建的_breed_ ,只是一种惯例,通过setter可以改变对变量访问值的处理规则。

如果不用_breed_,setter/getter的调用执行时机
class Dogger {
    constructor (name, breed) {
        this.name = name;
        this.breed = breed;
    }
    set breed (breed) {
        console.log("setter");
        this.breed = breed;
    }
    get breed () {
        console.log("getter");
        return this.breed;
    }
}

var dogger = new Dogger("忠犬八公", "柴犬");

代码报错了!!!这是因为,在构造函数中执行this.breed = breed的时候,就会去调用set breed,在set breed方法中,我们又执行this.breed = breed,进行无限递归,最后导致栈溢出(RangeError)

因此,原来只要this.breed中的属性名和set breed/get breed后面的breed一致,对this.breed就会调用setter/getter,也就是说setter/getter是hook函数,而真实的存储变量是_breed_,我们可以在代码中直接获取它。

ES6 的 proxy

Proxy可以理解成代理代办,在目标对象之前架设一层“拦截”,劫持了外界对该对象的访问和设置(setter和getter)。

借用最近看到的例子直接看代码吧

const phoneHandler = {
      get (target,name) {
        console.log(`正在读取${name}`)
        //"0102101220".replace(/(d{3})(d{3})(d{4})/,"$1-$2-$3")
        //"010-210-1220"
        return target[name].replace(/([0-9]{3})(d{3})(d{4})/,"($1)-$2-$3")
      },

      set (target, name, value) {
        console.log(`正在设置${name}`)
        // .match正则表达式的方法:匹配所有数字,全局匹配
        target[name] = value.match(/[0-9]/g).join("")
      }
    }

    // 拦截对象,代理代办,对空对象的代理
    // 复杂对象 ajax 将代码放在proxy中
    const phoneNumber = new Proxy({}, phoneHandler)
    phoneNumber.phone = "电话:0102101220"
    console.log(phoneNumber.phone)

不难看出new出来的Proxy是对空对象的代理,这样一来,setter和getter都被phoneHandler中的set和get包办了,用于复杂对象, ajax, 将代码放在proxy中代理。

参考

你不知道的javascript

javascript高级程序设计

求star:https://github.com/renjie1996...

qq:2578370399

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/82999.html

相关文章

  • Vue入门--基础语法

    摘要:在模板中放入太多的逻辑会让模板过重且难以维护。它会根据控件类型自动选取正确的方法来更新元素。指令需要使用的语法,指的是原数据数组,指的是迭代的数组元素。 注:本教程所使用的vue版本为 2.5.16 MVC与MVVM MVC(Model-View-Controller): M指的是从后台获取到的数据, V指的是显示动态数据的html页面, C是指响应用户操作、经过业务逻辑处理后去更新...

    haoguo 评论0 收藏0
  • Vue 基础语法

    摘要:在生成的项目中,我们打开文件夹下组件,为便于演示,删减了一部分内容本文中的例子都将改造组件来演示语法一创建组件在中,有好几种方式用来创建组件,后面会单独写一篇文章来介绍。表达式插值中也支持表达式插值用双大括号将表达式括起来。 Vue 的官方文档写的非常棒(另一个我觉得中文文档写的很好地是 Ant-Design)。 这篇文章以使用 vue-cli 生成的项目为基础,以完整 demo 的...

    saucxs 评论0 收藏0
  • Vue.js基础详解

    摘要:指令带有前缀,以表示它们是提供的特殊属性。最后,我们需要为赋值世界舞王尼古拉斯赵四世界舞王尼古拉斯赵四初学就到这里了,相信你已经在脑子里确定了的原理的概念也已经非常清楚了,希望你能够在学习的道路上越走越远,最后感谢你的浏览。 vue.js vue介绍 Vue.js(读音 /vjuː/,类似于 view) 是一套构建用户界面的渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量...

    omgdog 评论0 收藏0
  • 后端开发者的Vue学习之路(一)

    摘要:如果我们作为一个后端开发者想掌握一个前端框架,是一个好选择,因为它足够的易学。是语言的下一代标准。数据方法生命周期钩子函数其他有些内容比较重要,留到后面讲定义数据定义数据定义了数据,那么就可以在管理的区域中使用的获取数据的语法来获取数据。目录 前言: iview组件库示例 element组件库示例 ...

    番茄西红柿 评论0 收藏0
  • JavaScript 进阶之深入理解数据双向绑定

    摘要:当我们的视图和数据任何一方发生变化的时候,我们希望能够通知对方也更新,这就是所谓的数据双向绑定。返回值返回传入函数的对象,即第一个参数该方法重点是描述,对象里目前存在的属性描述符有两种主要形式数据描述符和存取描述符。 前言 谈起当前前端最热门的 js 框架,必少不了 Vue、React、Angular,对于大多数人来说,我们更多的是在使用框架,对于框架解决痛点背后使用的基本原理往往关注...

    sarva 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<