对象是无序属性的集合,而这些属性在创建是都带有一些特征值(可以理解为属性的属性,天生自带的),这些特征值是为了实现JavaScript引擎用的,因此JavaScript不能直接访问。

JavaScript通过这些特征值来定义属性的行为(属性是否删除,枚举,修改等)。

例如,在全局定义的属性是会挂载到window上的。当想删除window上的这个属性,是不可以的。也就是说window上的属性是不可配置的。delete window.obj     //false

Function.prototype当你修改成其他值,其原始值并没有改变。是不可写的。

在比如,我们的for in是可以枚举原型链上属性的,但所有的原型顶端都是Object.prototype.但for in 并没有枚举出来。所以Object.prototype是不可枚举的。

属性分为两种类型:1数据属性 2访问器属性。例如:一般我们自己在对象设置的属性默认是数据属性,而Window上的那么属性是访问器属性。

怎样知道这个属性到底是数据属性还是访问器属性?

使用Object.getOwnPropertyDescriptor(属性所在的对象,属性)方法。 返回一个对象,当时访问器属性时,该对象属性有enumerable,configurable,get,set。当时数据属性,该对象返回的属性有value,writable,enumerable,configurable.

var obj  = {name:'zwq',age:18};
console.log(Object.getOwnPropertyDescriptor(obj,'name')); //name属性属性
//{value: "zwq", writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(window,'name')); //window上的name属性时访问器属性
//enumerable: true, configurable: true, get: ƒ, set: ƒ}

数据属性

  • 数据上行包含一个数据值的位置,可以读取和写入值,数据属性有4个描述其行为的属性,由于是这些值不能直接访问,是内部值,所以该规范把他们放在两对括号中。
  • 属性是否可配置:[[Configurable]]:表能否通过delete删除属性,能够修改属性的特性,能否把属性修改为访问器属性
  • 属性是否可枚举:[[Enumerable]]:表能否通过for-in循环返回属性
  • 属性是否可修改:[[Writable]]
  • 属性的数据值 :[[value]]读取属性的时候,从这个位置读,写入属性的时候,把新值保存到这个位置。

普通定义的属性 默认值前三个都是true,最后一个是undefined。

当我们想到修改属性默认的特性,使用Object.defineProperty(属性所在对象,属性的名字,描述符对象)方法。

当使用Object.defineProperty方法第二个参数属性的名字不存在时,该方法会创建这个属性,并且该属性的特性除了value剩下的特性的默认值都是false。也就是说当你想让这个用Object.defineProperty方法创建的属性跟正常的属性一样可枚举,配置,写入,必须把这个属性值的特性都改为true。否则就是false。

     var obj  = {name:'zwq',age:18};
Object.defineProperty(obj,'name',{ 修改name属性的特性,值为haha,并且name属性不能修改值
value:'haha',
writable:false //默认值是true,改为false,不可写。
})
Object.defineProperty(obj,'sex',{创建一个sex属性,这个属性不可枚举
value:'woman',
writable:true,
configurable:true,
})

访问器属性

访问器属性不包含writable和value,他包含的是一对getter和setter函数,在读取访问器属性是,会调用getter函数,并返回有效的值,在写入访问器属性时(修改属性)会调用setter函数并传入新值。访问器包含4个特性

  • 属性是否可配置:[[Configurable]]:表能否通过delete删除属性,能够修改属性的特性,能否把属性修改为访问器属性
  • 属性是否可枚举:[[Enumerable]]:表能否通过for-in循环返回属性
  • [[Get]]:在读取属性时调用的函数。默认值是undefined。
  • [[Set]]:在写入(或修改)属性时调用的函数。默认值undefined。

定义访问器属性,同样也必须商用Object.defineProperty().

     function Person(){
this._name = 'zwq',
this.age = 18
}
var person = new Person(); Object.defineProperty(person,'name',{
set(newValue){
console.log('set');
this._name = newValue //设置或修改属性时,会调用set函数,把设置的值通过参数传进去后,用一个变量或属性保存。并且当调用get,return就是返回的这个值
},
get(){
return this._name; //当读取属性时 返回return的值
} })

不一定非要同时指定getter和setter,只指定getter意味着属性是不能写。

vue的双向数据劫持绑定(主要应用于表单中)的原理就是利用Object.defineProperty来检测数据的变化。

双向劫持绑定时当视图(页面的某一元素)发生改变时,数据跟着改变,当数据改变时,视图也跟着改变。例如下面的输入框里面的内容改变时,数据(对象或数组)改变。检测数据改变。底下的div文本根据数据的改变而改变。

上面我们介绍到,当数据改变时会触发set方法。由此我们就可以检测数据的变化。

    //检测对象的变化。
     var input = document.getElementById('Oinput');
var view = document.getElementById('view'); var data = {
valueObj :{
value:'zwq'
}
}
//当输入框数据发生改变时,数据跟着改变
input.oninput = function(){
data.valueObj.value = this.value;
} // 更新视图
function upData(){
view.innerText = data.valueObj.value;
} upData(data);
obServe(data); // 监控某个对象是否发生改变
function obServe(data){
//判断当前传的是否是对象,如果不是,直接return
if(!data || !(data instanceof Object)){return data}
      //获取所有属性名。使用keys方法可以获取所有属性名(包括原型上的)并保存带数组中
var arrProperty = Object.keys(data);
       //遍历数组,调用defindRective检测每一个属性值的改变 
arrProperty.forEach(function(key){
          defindRective(data,key,data[key]); //传入3个参数,当前对象,当前属性,当前属性值
})
} function defindRective(obj,key,val){
obServe(val); //使用递归,当想上面的数组,对象套对象的形式,由于里面的对象是一个引用值,无法检测里面的数据变化,所以使用递归。
Object.defineProperty(obj,key,{ //核心:使用Object,definPropert的set检测数据的改变。
set(newValue){
console.log(5);
if(newValue == val) return val;
val = newValue;
upData(); //当数据变化,跟新视图
},
get(){
return val;
}
})
}
// 监测数组,将数组原型重写
// 当操作数组的arr push pop unshift slice...才会检测
let {push} = Array.prototype;
var arr = [1,2,3];
function upData(){
console.log('更新');
}
Object.defineProperty(Array.prototype,'push',{
value:(function(){
return (...arg) => {
upData();
push.apply(arr,arg);
}
})()
})
arr.push(8,9);

由于使用Object.defineProperty检测数组和对象变化要分开实现。而且,当添加数据时,不会检测到的。

所以ES6中添加了Proxy来实现。

Proxy&reflect简介:植入代理的模式,以简洁易懂的方式控制对外部对象的访问,利用set,get方法控制属性的读写功能,还有其余的has,desProperty。。等方法。但proxy兼容性不好,使用时要注意。

proxy是一个构造函数,通过代理的方式将你想到代理的对象传给构造函数,并且需要传入参数对象对读和写进行控制。使用new方法实例化代理的对象,

此后,当修改,或添加属性都使用代理的对象。

let data = {
value:'zwq',
}
// let data = [1,2];
let oProxyData = new Proxy(data,{
set(target,key,value,receiver){ //传入4个参数 对象 属性 属性值 代理的对象
// target[key] = value;
Reflect.set(target,key,value); //等同于上一步
upData();
  },
get(target,key,receiver){
// console.log(target,key);
Reflect.get(target, key);
},
has(target,key){ //当使用in时触发当前函数。
return key in target; //in --检测对象能否访问该属性,能访问返回true,不能false,无论是在实例还是原型中。
}
}); console.log('valu' in oProxyData); function upData(){
console.log("更新啦");
}
oProxyData.value = 20;    

JavaScript 之 对象属性的特性 和defineProperty方法的更多相关文章

  1. JavaScript之面向对象的概念,对象属性和对象属性的特性简介

    一.大家都知道,面向对象语言有一个标志,那就是他们都有类的概念,通过类我们可以创建任意多个具有相同属性和方法的对象.但ECMAScript(指定JavaScript标准的机构,也就是说JavaScri ...

  2. 深入理解JavaScript中的属性和特性

    深入理解JavaScript中的属性和特性 JavaScript中属性和特性是完全不同的两个概念,这里我将根据自己所学,来深入理解JavaScript中的属性和特性. 主要内容如下: 理解JavaSc ...

  3. JavaScript 访问对象属性和方法及区别

    这篇文章主要介绍了浅析JavaScript访问对象属性和方法及区别的相关资料,仅供参考 属性是一个变量,用来表示一个对象的特征,如颜色.大小.重量等:方法是一个函数,用来表示对象的操作,如奔跑.呼吸. ...

  4. php删除多重数组对象属性,重新赋值的方法

    php删除多重数组对象属性,重新赋值的方法 实例:sphinx搜索出来的结果,要去掉某个属性值:$cl = new SphinxClient ();$query = $cl->Query ( $ ...

  5. JavaScript 操作对象属性(设置属性, setter/getter, 序列化)

    参考自<<JavaScript权威指南 第6版>>, 文字太多反而不易理清其中的关系, 直接上代码和注释 /* * 对象的setter和getter属性: * 定义为一个或者两 ...

  6. javascript 理解对象--- 属性类型

    ECMA-262 把对象定义为:无序属性的集合,其属性可以包含基本值.对象或者函数: var Person = { name:"wsc", age :"25", ...

  7. Javascript设置对象属性为"只读"

    有时为了保护某些属性,让其无法被更改,我们会把他们设置为常量. 在某些语言里面,也许会用const来实现这样的功能.本文讲述如何在Javascript中实现这样的功能. 方法一: var myObje ...

  8. 转: JavaScript 获取对象属性和方法

    一.获取对象属性和方法 Object.keys()for in 返回对象的可枚举属性和方法的名称数组. Object.getOwnPropertyNames() 返回的数组的所有属性(可枚举或不可枚举 ...

  9. JavaScript读取对象属性遇到的问题

    JavaScript中对于对象的属性存取方式有两种:“.”操作和[]操作. “.”操作属性名通常直接写,[]操作中属性的名字通常要加引号, 而当需要读取的对象属性名是一个变量的时候,一般使用[]操作, ...

随机推荐

  1. .NET Core简单使用RabbitMq

    RabbitMQ简介 RabbitMQ是一个开源的,基于AMQP(Advanced Message Queuing Protocol)协议的完整的可复用的企业级消息队,RabbitMQ可以实现点对点, ...

  2. 分享一个Vue数组赋值的错误

    今天在写项目用到Vue的时候,遇到的一个问题,纠结了好一会,首先我的代码是这样的 有没有毛病!!  开始我感觉是没啥毛病啊,按照之前写Java代码的逻辑,我感觉这没一点毛病 . 但是它就是有毛病, 假 ...

  3. SpringBoot-配置文件相关(五)

    SpringBoot-配置文件 SpringBoot官方文档 配置相关 SpringBoot使用一个全局的配置文件 , 配置文件名称是固定的 application.properties 语法结构 : ...

  4. Prometheus笔记(一)metric type

    欢迎加入go语言学习交流群 636728449 Prometheus笔记(二)监控go项目实时给grafana展示 Prometheus笔记(一)metric type 文章目录 Prometheus ...

  5. 学好linux必须精通用户管理的章节知识

    第12章 Linux中用户知识管理 12.1 系统开机启动流程 12.1.1 centos6系统开机启动流程 12.1.1.1 开机系统流程语言描述 服务器电源开关打开 bios自检 目的:检查硬件是 ...

  6. ARTS-S pytorch中backward函数的gradient参数作用

    导数偏导数的数学定义 参考资料1和2中对导数偏导数的定义都非常明确.导数和偏导数都是函数对自变量而言.从数学定义上讲,求导或者求偏导只有函数对自变量,其余任何情况都是错的.但是很多机器学习的资料和开源 ...

  7. 深入学习 Arduino LinkedList库(一个变长的集合类数组)

    QQ技术互动交流群:ESP8266&32 物联网开发 群号622368884,不喜勿喷 单片机菜鸟博哥CSDN 1.前言 博主是做Android App开发出身的,所以对Java这门语言算是有 ...

  8. rails 创建项目、创建controller、model等

    rails2之前创建新项目: rails3以及更高版本创建新项目:rails new webname 创建数据表model:rails g model user name:string sex:str ...

  9. Spring与Shiro整合 加载权限表达式

    Spring与Shiro整合 加载权限表达式 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 如何加载权限表达式  我们在上章内容中画了一张图,里面有三个分项,用户 角色 权限: 那 ...

  10. AQS系列(三)- ReentrantReadWriteLock读写锁的加锁

    前言 前两篇我们讲述了ReentrantLock的加锁释放锁过程,相对而言比较简单,本篇进入深水区,看看ReentrantReadWriteLock-读写锁的加锁过程是如何实现的,继续拜读老Lea凌厉 ...