JS之对象(2)

《JS之对象(2)》

媒介

一篇完全搞懂对象,今后不必忧郁没对象啦;

本文从对象定义要领,对象属性,Symbol数据范例,遍历几种要领,对象拷贝,vue2.x和vue3.x阻拦对象属性要领及代码完成几个方面由浅入深引见对象

1.对象的声明要领

1.1 字面量

var test2 = {x:123,y:345};
console.log(test2);//{x:123,y:345};
console.log(test2.x);//123
console.log(test2.__proto__.x);//undefined
console.log(test2.__proto__.x === test2.x);//false

1.2 组织函数

var test1 = new Object({x:123,y:345});
console.log(test1);//{x:123,y:345}
console.log(test1.x);//123
console.log(test1.__proto__.x);//undefined
console.log(test1.__proto__.x === test1.x);//false

new的作用:
1.创了一个新对象;
2.this指向组织函数;
3.组织函数有返回,会替代new出来的对象,假如没有就是new出来的对象

1.3 内置要领

Obejct.create(obj,descriptor),obj是对象,describe描述符属性(可选)

let test = Object.create({x:123,y:345});
console.log(test);//{}
console.log(test.x);//123
console.log(test.__proto__.x);//3
console.log(test.__proto__.x === test.x);//true

1.4 三种要领的优缺点

1.功用:都能完成对象的声明,并能够赋值和取值
2.继续性:内置要领建立的对象继续到__proto__属性上
3.隐蔽属性:三种声明要领会默以为内部的每一个成员(属性或要领)天生一些隐蔽属性,这些隐蔽属性是能够读取和可设置的,属性分类见下面
4.属性读取:Object.getOwnPropertyDescriptor()或getOwnPropertyDescriptor()
5.属性设置:Object.definePropertype或Object.defineProperties

2.对象的属性

2.1 属性分类

1.数据属性4个特征:
configurable(可设置),enumerable(可罗列),writable(可修正),value(属性值)

2.接见器属性2个特征:
get(猎取),set(设置)

3.内部属性
由JavaScript引擎内部运用的属性;
不能直接接见,然则能够经由过程对象内置要领间接接见,如:[[Prototype]]能够经由过程 Object.getPrototypeOf()接见;
内部属性用[[]]围困示意,是一个笼统操纵,没有对应字符串范例的属性名,如[[Prototype]].

2.2 属性描述符

1.定义:将一个属性的一切特征编码成一个对象返回
2.描述符的属性有:数据属性和接见器属性
3.运用范围:
作为要领Object.defineProperty, Object.getOwnPropertyDescriptor, Object.create的第二个参数,

2.3 属性描述符的默许值

1.接见对象存在的属性

特征名默许值
value对应属性值
get对应属性值
setundefined
writabletrue
enumerabletrue
configurabletrue

所以经由过程上面三种声明要领已存在的属性都是有这些默许描述符
2.接见对象不存在的属性

特征名默许值
valueundefined
getundefined
setundefined
writablefalse
enumerablefalse
configurablefalse

2.3 描述符属性的运用划定规矩

get,set与wriable,value是互斥的,假如有交集设置会报错

2.4 属性定义

1.定义属性的函数有两个:Object.defineProperty和Object.defineProperties.比方:
Object.defineProperty(obj, propName, desc)

2.在引擎内部,会转换成如许的要领挪用:
obj.[[DefineOwnProperty]](propName, desc, true)

2.5 属性赋值

1.赋值运算符(=)就是在挪用[[Put]].比方:
obj.prop = v;

2.在引擎内部,会转换成如许的要领挪用:
obj.[[Put]](“prop”, v, isStrictModeOn)

2.6 推断对象的属性

称号寄义用法
in假如指定的属性在指定的对象或其原型链中,则in 运算符返回true‘name’ in test //true
hasOwnProperty()只推断本身属性test.hasOwnProperty(‘name’) //true
.或[]对象或原型链上不存在该属性,则会返回undefinedtest.name //”lei” test[“name”] //”lei”

3.Symbol

3.1观点

是一种数据范例;
不能new,由于Symbol是一个原始范例的值,不是对象。

3.2 定义要领

Symbol(),能够传参

var s1 = Symbol();
var s2 = Symbol();
s1 === s2 // false

// 有参数的状况
var s1 = Symbol("foo");
var s2 = Symbol("foo");
s1 === s2 // false

3.3 用法

1.不能与其他范例的值举行运算;
2.作为属性名

let mySymbol = Symbol();

// 第一种写法
var a = {};
a[mySymbol] = 'Hello!';

// 第二种写法
var a = {
  [mySymbol]: 'Hello!'
};

// 第三种写法
var a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上写法都获得一样效果
a[mySymbol] // "Hello!"

3.作为对象属性名时,不能用点运算符,能够用[]

let a = {};
let name = Symbol();
a.name = 'lili';
a[name] = 'lucy';
console.log(a.name,a[name]); 

4.遍历不会被for…in、for…of和Object.keys()、Object.getOwnPropertyNames()取到该属性

3.4 Symbol.for

1.定义:在全局中搜刮有无以该参数作为称号的Symbol值,假如有,就返回这个Symbol值,不然就新建并返回一个以该字符串为称号的Symbol值
2.举例:

var s1 = Symbol.for('foo');
var s2 = Symbol.for('foo');
s1 === s2 // true

3.5 Symbol.keyFor

1.定义:返回一个已登记的Symbol范例值的key
2.举例:

var s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"

var s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined 

4.遍历

4.1 一级对象遍历要领

要领特征
for … in遍历对象本身的和继续的可罗列属性(不含Symbol属性)
Object.keys(obj)返回一个数组,包括对象本身的(不含继续的)一切可罗列属性(不含Symbol属性)
Object.getOwnPropertyNames(obj)返回一个数组,包括对象本身的一切可罗列属性(不含Symbol属性)
Object.getOwnPropertySymbols(obj)返回一个数组,包括对象本身的一切Symbol属性
Reflect.ownKeys(obj)返回一个数组,包括对象本身的一切(不罗列、可罗列和Symbol)属性
Reflect.enumerate(obj)返回一个Iterator对象,遍历对象本身的和继续的一切可罗列属性(不含Symbol属性)

总结:1.只要Object.getOwnPropertySymbols(obj)和Reflect.ownKeys(obj)能够拿到Symbol属性
2.只要Reflect.ownKeys(obj)能够拿到不可罗列属性

4.2 多级对象遍历

数据模型:

var treeNodes = [
    {
     id: 1,
     name: '1',
     children: [
       {
        id: 11,
        name: '11',
        children: [
         {
          id: 111,
          name: '111',
          children:[]
          },
          {
            id: 112,
            name: '112'
           }
          ]
         },
         {
          id: 12,
          name: '12',
          children: []
         }
         ],
         users: []
        },
      ];

递归:

var parseTreeJson = function(treeNodes){
      if (!treeNodes || !treeNodes.length) return;

       for (var i = 0, len = treeNodes.length; i < len; i++) {

            var childs = treeNodes[i].children;

            console.log(treeNodes[i].id);

            if(childs && childs.length > 0){
                 parseTreeJson(childs);
            }
       }
    };

    console.log('------------- 递归完成 ------------------');
    parseTreeJson(treeNodes);

5.深度拷贝

5.1 Object.assign

1.定义:将源对象(source)的一切可罗列属性,复制到目的对象(target)
2.用法:

兼并多个对象
var target = { a: 1, b: 1 };
var source1 = { b: 2, c: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);

3.注重:
这个是伪深度拷贝,只能拷贝第一层

5.2 JSON.stringify

1.道理:是将对象转化为字符串,而字符串是简朴数据范例

5.3 递归拷贝

function deepClone(source){
  const targetObj = source.constructor === Array ? [] : {}; // 推断复制的目的是数组照样对象
  for(let keys in source){ // 遍历目的
    if(source.hasOwnProperty(keys)){
      if(source[keys] && typeof source[keys] === 'object'){ // 假如值是对象,就递归一下
        targetObj[keys] = source[keys].constructor === Array ? [] : {};
        targetObj[keys] = deepClone(source[keys]);
      }else{ // 假如不是,就直接赋值
        targetObj[keys] = source[keys];
      }
    }
  }
  return targetObj;
}  


6.数据阻拦

定义:应用对象内置要领,设置属性,进而转变对象的属性值

6.1 Object.defineProterty

1.ES5出来的要领;
2.三个参数:对象(必填),属性值(必填),描述符(可选);
3.defineProterty的描述符属性

数据属性:value,writable,configurable,enumerable
接见器属性:get,set
注:不能同时设置value和writable,这两对属性是互斥的

4.阻拦对象的两种状况:

let obj = {name:'',age:'',sex:''  },
    defaultName = ["这是姓名默许值1","这是岁数默许值1","这是性别默许值1"];
  Object.keys(obj).forEach(key => {
    Object.defineProperty(obj, key, {
      get() {
        return defaultName;
      },
      set(value) {
        defaultName = value;
      }
    });
  });

  console.log(obj.name);
  console.log(obj.age);
  console.log(obj.sex);
  obj.name = "这是转变值1";
  console.log(obj.name);
  console.log(obj.age);
  console.log(obj.sex);

  let objOne={},defaultNameOne="这是默许值2";
  Object.defineProperty(obj, 'name', {
      get() {
        return defaultNameOne;
      },
      set(value) {
        defaultNameOne = value;
      }
  });
  console.log(objOne.name);
  objOne.name = "这是转变值2";
  console.log(objOne.name);

5.阻拦数组变化的状况

let a={};
bValue=1;
Object.defineProperty(a,"b",{
    set:function(value){
        bValue=value;
        console.log("setted");
    },
    get:function(){
        return bValue;
    }
});
a.b;//1
a.b=[];//setted
a.b=[1,2,3];//setted
a.b[1]=10;//无输出
a.b.push(4);//无输出
a.b.length=5;//无输出
a.b;//[1,10,3,4,undefined];

结论:defineProperty没法检测数组索引赋值,转变数组长度的变化;
    然则经由过程数组要领来操纵能够检测到


6.存在的题目

不能监听数组索引赋值和转变长度的变化
必需深层遍历嵌套的对象,由于defineProterty只能挟制对象的属性,因而我们须要对每一个对象的每一个属性举行遍历,假如属性值也是对象那末须要深度遍历,显然能挟制一个完全的对象是更好的挑选

6.2 proxy

1.ES6出来的要领,本质是对对象做了一个阻拦,并供应了13个处置惩罚要领
13个要领详情请戳,阮一峰的proxy引见

2.两个参数:对象和行动函数

let handler = {
    get(target, key, receiver) {
      console.log("get", key);
      return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver) {
      console.log("set", key, value);
      return Reflect.set(target, key, value, receiver);
    }
  };
  let proxy = new Proxy(obj, handler);
  proxy.name = "李四";
  proxy.age = 24;

3.题目和长处
reflect对象没有组织函数
能够监听数组索引赋值,转变数组长度的变化,
是直接监听对象的变化,不必深层遍历

6.3 defineProterty和proxy的对照

1.defineProterty是es5的规范,proxy是es6的规范;

2.proxy能够监听到数组索引赋值,转变数组长度的变化;

3.proxy是监听对象,不必深层遍历,defineProterty是监听属性;

3.应用defineProterty完成双向数据绑定(vue2.x采纳的中心)
请戳,理会Vue道理&完成双向绑定MVVM
4.应用proxy完成双向数据绑定(vue3.x会采纳)

    原文作者:火狼
    原文地址: https://segmentfault.com/a/1190000018303120
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞