javascript 基础 – 数据绑定的前奏之对象属性

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 ] ]( )。

  1. 在对象中查找有没有这个属性。
  2. 在该对象的原型链上查找有没有这个属性。
  3. 都没找到返回undefined(留意:假如那个属性值恰恰为undefined时,固然返回值一样,但是底层发作的事是不一样的

[ [ Put ] ]

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

  1. 首先肯定能否存在这个属性。breed能否存在
  2. 存在,能否是setter。是setter就调用setter,能否是setter来给breed赋值
  3. writable能否为false,false则无效。breed的writable能否为false
  4. 设置值为该属性的值

Getter和Setter

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

  1. set
  2. get
  3. configurable
  4. 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("忠犬八公", '柴犬');
  1. 代码报错了!!!这是由于,在结构函数中执行this.breed = breed的时分,就会去调用set breed,在set breed办法中,我们又执行this.breed = breed,停止无限递归,最后招致栈溢出(RangeError)
  2. 因而,原来只需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 基础 – 数据绑定的前奏之对象属性

赞 (1)

评论 0

评论前必须登录!

登陆 注册