资讯专栏INFORMATION COLUMN

JS之对象(2)

CarterLi / 414人阅读

摘要:前言一篇彻底搞懂对象从此不用担心没对象啦本文从对象定义方法对象属性数据类型遍历几种方法对象拷贝和拦截对象属性方法及代码实现几个方面由浅入深介绍对象对象的声明方法字面量构造函数的作用创了一个新对象指向构造函数构造函数有返回会替换出来的对象如果

前言
一篇彻底搞懂对象,从此不用担心没对象啦;
本文从对象定义方法,对象属性,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 对应属性值
set undefined
writable true
enumerable true
configurable true

所以通过上面三种声明方法已存在的属性都是有这些默认描述符
2.访问对象不存在的属性

特性名 默认值
value undefined
get undefined
set undefined
writable false
enumerable false
configurable false
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
.或[] 对象或原型链上不存在该属性,则会返回undefined test.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://www.ucloud.cn/yun/102133.html

相关文章

  • JS修仙一界本源

    摘要:时间一晃就是数月,齐云早已把界基本情况了解了,不过至于三座大山里隐藏的谜团却迟迟没有头绪。它是界本源之一。事实上,根本没有构造函数,或者人人都是构造函数。所以,明白这个界的一界本源,才能在这里畅游天地之间。 自计算机宇宙诞生以来,有很多大神通者在这里开天辟地,开创了很多界,有C、C++、Java等世界,它们彼此相连,其中有一处叫做JavaScript的世界,自被开辟以来吸引了很多修行者...

    I_Am 评论0 收藏0
  • JS面向对象二 【原型链】(对象对象间的关系)

    摘要:面向对象之二原型链对象和对象之间的关系注意这个系列文章要经常站在之父的视角去思考。思考问题我们都知道都属于那么既然他们都是对象一定有某些相同之处吧对象和对象之间有什么关联呢如果说你没有思考过这个问题那么可以换一个更具体的问题。 JS面向对象之二 【原型链】(对象和对象之间的关系) 注意这个系列文章,要经常站在JS之父的视角去思考。 牢记我们的需求,我要在JS没有class的情况下,那么...

    Jochen 评论0 收藏0
  • JS面向对象三【this】 (对象和函数间的关系)

    摘要:实际上就是做了这样一件事情显式的指定是回顾一下隐式模式显示模式观点里所有函数都接受个参数第一个第二个是函数被调用时一定会有这个参数如果你用调用函数就是显式的传递和如果你用语法直接调用函数那就去帮你偷偷的传递。 JS面向对象之三【this】 (对象和函数之间的关系) 上一篇,谈了对象和对象的关系,现在我们谈谈对象和函数的关系 先说结论,也就是观点1 观点1: JS里函数和对象没有关系,J...

    roland_reed 评论0 收藏0
  • JS14种设计模式 (6)

    摘要:序列文章面试之函数面试之对象面试之数组的几个不操作面试之对比分析面试之数据结构与算法前言设计模式如果应用到项目中,可以实现代码的复用和解耦,提高代码质量。 showImg(https://segmentfault.com/img/bVbq2VA?w=480&h=260); 序列文章 JS面试之函数(1)JS面试之对象(2)JS面试之数组的几个不low操作(3)JS面试之http0.9~...

    luckyyulin 评论0 收藏0
  • 好程序员Web前端培训入门JS基础知识梳理汇总

    摘要:好程序员前端培训入门之基础知识梳理汇总,前端工程师是当前各大企业都比较稀缺的人才,薪资待遇和就业前景都很不错。作用域链的前端,始终是当前执行代码所在环境的变量对象。   好程序员Web前端培训入门之JS基础知识梳理汇总,Web前端工程师是当前各大企业都比较稀缺的人才,薪资待遇和就业前景都很不错。不论是专业还是非专业,有基础亦或是无基础,都想通过学习Web前端实现高薪就业。不过,学习要一...

    int64 评论0 收藏0
  • 好程序员Web前端培训入门JS基础知识梳理汇总

    摘要:好程序员前端培训入门之基础知识梳理汇总,前端工程师是当前各大企业都比较稀缺的人才,薪资待遇和就业前景都很不错。作用域链的前端,始终是当前执行代码所在环境的变量对象。   好程序员Web前端培训入门之JS基础知识梳理汇总,Web前端工程师是当前各大企业都比较稀缺的人才,薪资待遇和就业前景都很不错。不论是专业还是非专业,有基础亦或是无基础,都想通过学习Web前端实现高薪就业。不过,学习要一...

    kviccn 评论0 收藏0

发表评论

0条评论

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