Object.defineProperty()的学习了解

背景

最近在总结vue系列的时候是看到响应原理的时候,看到一个新的知识点也就是我们的标题Object.defineProperty()的时候,好了话不多说,我们来看看这个是怎么使用的

开始

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。

上面这句话是MDN上写的,当你看到这句话的时候有什么感觉吗?没有?其实我看到这句话的时候确实也没什么感觉,那怎么办我们在看一边,哦发现了,cao,添加属性什么时候要怎么麻烦了写一个这么长的代码啊,我以前都是这样写的

构造法

var obj = new Object;  //obj = {}
obj.name = "张三";  //添加描述
obj.say = function(){};  //添加行为

字面量法

var obj = {
    name : "张三",
    say  : function(){}
}

对吧什么时候用过上面的方法啊?其实前端就是这样,别人说简单只是他没深入,只是他cai,就HTML,winter他都说难,css,张鑫旭都是工作了10年才出的书(css世界,还没看完可惜,呵呵),对吧所以。。。。大家懂了吧呵呵,下次那个B在说前端简单给我打电话去干他,呵呵。

语法

我们来看下object.defineProperty的语法从而去了解他和我们以前的写法有什么不一样

作用

一:针对属性,我们可以给这个属性设置一些特性,比如是否只读不可以写(不可以修改);是否可以被for..inObject.keys()遍历(不会被被枚举遍历),用object.defineProperty写出来的属性,默认值是不可修改,和不会被遍历出来的,(后面会讲)

二:其实object.definePorperty的存取器描述有点绑定事件的感觉(后面会写)

参数

Object.defineProperty(obj, prop, descriptor)
obj :目标对象
prop :需要定义或修改的属性
descriptor :需要定义或修改的属性描述符

返回值

返回的是一个对象,就是我们修改后的obj对象

属性描述符

给对象的属性添加特性描述,目前提供两种形式:数据描述存取器描述。

数据描述符和存取描述符均共同具有以下可选键(属性)值:

configurable

当且仅当该属性的 configurable 为 true 时,该属性描述符(就是像configurable,enumerable之类的描述符)才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false

//-----------------测试目标属性是否能被删除------------------------
var obj = {}
//第一种情况:configurable设置为false,不能被删除。
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:false,
    enumerable:false,
    configurable:false
});
//删除属性
delete obj.newKey;
console.log( obj.newKey ); //hello

//第二种情况:configurable设置为true,可以被删除。
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:false,
    enumerable:false,
    configurable:true
});
//删除属性
delete obj.newKey;
console.log( obj.newKey ); //undefined

//-----------------测试是否可以再次修改特性------------------------
var obj = {}
//第一种情况:configurable设置为false,不能再次修改特性。
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:false,
    enumerable:false,
    configurable:false
});

//重新修改特性
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:true,
    enumerable:true,
    configurable:true
});
console.log( obj.newKey ); //报错:Uncaught TypeError: Cannot redefine property: newKey

//第二种情况:configurable设置为true,可以再次修改特性。
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:false,
    enumerable:false,
    configurable:true
});

//重新修改特性
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:true,
    enumerable:true,
    configurable:true
});
console.log( obj.newKey ); //hello

enumerable

当且仅当该属性的enumerabletrue时,该属性才能够出现在对象的枚举属性中。默认为 false

这也说明了为什么用object.defineProperty()的时候默认是不可修改,和不会被枚举出来的了

那么我们说明了共同点我们就来分别来说明他们直接的写法

数据描述符

value

该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。当value没有定义的时候,值为undefined, 和一般情况下一样的

writable

当且仅当该属性的writabletrue时,value才能被赋值运算符改变,重新重写成功,默认为 false

默认情况下在数据描述符写法下value的值是不可以随便改变的,

var obj = {}
//第一种情况:writable设置为false,不能重写。
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:false
});
//更改newKey的值
obj.newKey = "change value";
console.log( obj.newKey );  //hello

//第二种情况:writable设置为true,可以重写
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:true
});
//更改newKey的值
obj.newKey = "change value";
console.log( obj.newKey );  //change value

存储器描述符

get

一个给属性提供 getter(取) 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。

默认为 undefined

set

一个给属性提供 setter (存,设置)的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性(我们定义添加的属性)新的属性值(就是我更改的值)。

默认为 undefined

注意:当使用了getter或setter方法,不允许使用writable和value这两个属性,二种不同的用法,一般情况下第二种用的多

var obj = {};
var initValue = 'hello';
Object.defineProperty(obj,"newKey",{
    get:function (){
        //当获取值的时候触发的函数
        return initValue;    
    },
    set:function (value){
        //当设置值的时候触发的函数,设置的新值通过参数value拿到,这里value的值就是我们更改的值
        initValue = value;
    }
});
//获取值
console.log( obj.newKey );  //hello

//设置值
obj.newKey = 'change value';

console.log( obj.newKey ); //change value

注意:get或set不是必须成对出现,任写其一就可以。如果不设置方法,则get和set的默认值为undefined

看一个set和get使用的例子

下面的例子展示了如何实现一个自存档对象。 当设置temperature 属性时,archive 数组会获取日志条目。

function Archiver() {
  var temperature = null;
  var archive = [];

  Object.defineProperty(this, 'temperature', {
    get: function() {
      console.log('get!');
      return temperature;
    },
    set: function(value) {
      temperature = value;
      archive.push({ val: temperature });
    }
  });

  this.getArchive = function() { return archive; };
}

var arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]

如果你这个看懂了那就恭喜你了,应该是会了

object.defineproperty与继承之间的问题

如果访问者的属性是被继承的,它的 get 和set 方法会在子对象的属性被访问或者修改时被调用。如果这些方法用一个变量存值,该值会被所有对象共享。

function myclass() {
}

var value;
Object.defineProperty(myclass.prototype, "x", {
  get() {
    return value;
  },
  set(x) {
    value = x;
  }
});

var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(b.x); // 1

 

那么如何结局上面的问题:在继承的对象中添加的属性值会被继承

在解决问题的时候我们先分析问题:这里的访问者就是a和b他们的x属性就是继承过来的,那么a或b中的x的属性值发生改变的时候他们继承的get方法和set方法会触发,所以有引发了下一个问题(get或set函数中的this的指向),那么这里的this的指向就是他们的访问者,是访问值触发的get或set函数

那么解决问题的方法就是,当触发set的时候给自己对于的访问者添加属于自己的属性值。

function myclass() {
}

Object.defineProperty(myclass.prototype, "x", {
  get() {
    return this.stored_x;
  },
  set(x) {
    this.stored_x = x;
  }
});

var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(b.x); // undefined

注意:一般情况下在原型(prototype)中建立的属性值,与new出来的实例中的属性值是没有关系的,但是在object.defineProperty中如果一个不可写的属性被继承,它仍然可以防止修改对象的属性

------一般情况下------

 function myclass() {}
 myclass.prototype.b = 10;
 var b = new myclass();
 console.log(b.b); // 10
 b.b =2;
 console.log(b.b); // 2 这里发生了改变
 console.log(b.prototype.b) //10  

------object.defineProperty-------
function myclass() {
}
Object.defineProperty(myclass.prototype, "y", {
  writable: false,
  value: 1
});

var a = new myclass();
a.y = 2; // Ignored, throws in strict mode
console.log(a.y); // 1 这里没有改变
console.log(myclass.prototype.y); // 1 

 这里的y属性就是防止被修改。

好了这里就是这篇快结束了,那么和vue的响应式有什么关系呢?当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 object.defineproperty 把这些属性全部转为 getter/setter.这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化,其实就是model改变后通过object.defineproperty来改变view中的值,来达到单向数据流,而不是双向绑定(vue中不是双向绑定)

猜你喜欢

转载自blog.csdn.net/qq_39148344/article/details/89082741