YDKJS-对象那些事儿

Begin

要知道此对象非彼对象....

上一篇this那些事儿介绍了this的指向,this的指向是一个对象。所以很自然地提出在JavaScript中对象是什么呢?除了众所周知关于对象的知识以外,有没有什么是容易被忽略却是有趣而有用?

让我们开始吧!

介绍一下object家族的基本情况

JavaScript基本简单类型可以认为是6位成员,分别是:stringnumberbooleannullundefined,当然还有objectobject这一脉又有对象子类型,又被称为复杂基本类型,认识一些特殊的对象子类型:

  1. function,在js中被称为"头等"类型,因为它们基本上就是普通的对象。对象子类型中的内建对象也是function,它的地位很特殊;

  2. 还有九种内建对象:StringNumberBooleanObjectFunctionArrayRegExpDateError。强烈推荐尽可能地使用字面形式的值,而非使用构造的对象形式,因为更加简单方便。像持有字符串、数字或者布尔值的变量去调用属性和方法,引擎会自动地将它转换为对象,比如:

    var strPrimitive = "I am a string";
    
    strPrimitive.length; //13
     strPrimitive.charAt( 3 )  //"m"
    复制代码

深入了解object

object的属性访问

属性访问是对象的特殊能力,它有两种方法,.操作符和[]操作符。

  • .操作符:后面只能接标识符(字母、数字、下划线,且数字不能在首位);

  • []操作符:[]内放的是字符串的值,可以是obj["b"],也可以是var a="b";obj[a]通过一个变量返回,间接的方式

    • []操作符另一个使用是计算型属性名,在键声明位置指定一个表达式:

      var prefix = "foo";
      
      var myObject = {
      	[prefix + "bar"]: "hello",
      	[prefix + "baz"]: "world"
      };
      
      myObject["foobar"]; // hello
      myObject["foobaz"]; // world
      复制代码
复制对象

复制对象分浅拷贝和深拷贝。

  • 浅拷贝:Object.assign(){...obj}

  • 深拷贝:var newObj = JSON.parse( JSON.stringify( someObj ) );

    • 深拷贝的一个应用是数组去重,尤其是可以处理数组元素存在数组或者对象情况:

      //数组去重
      function arrElementOnly(arr) {
        arr = arr.map(ele => JSON.stringify(ele));
        let returnArr = [];
      
        arr.forEach(ele => {
          if (returnArr.indexOf(ele) === -1) returnArr.push(ele);
        })
      
        return returnArr.map(ele => JSON.parse(ele));
      }
      
      var a = [1, 1, 2];		
      arrElementOnly(a);	//[1,2]
      
      var b = [1, [1, 2],
        [1, 2]
      ];
      arrElementOnly(b);	//[1,[1,2]]
      
      var c = [1, {
        a: "1"
      }, {
        a: "1"
      }];
      arrElementOnly(c);	//[1,{a:"1"}]
      
      var d = [1, 1, 2, [1, 2],
        [1, 2], {
          a: "1"
        }, {
          a: "1"
        }
      ];
      arrElementOnly(d);	//[1,2,[1,2],{a:"1"}]
      复制代码
属性描述符

属性描述符分为数据描述符和访问器描述符。数据描述符使用Object.getOwnPropertyDescriptor(...)查看,使用Object.defineProperty(...)定义,看个例子:

var myObject = {
	a: 2
};

Object.getOwnPropertyDescriptor( myObject, "a" );	//查看数据描述符
// {
//    value: 2,
//    writable: true,
//    enumerable: true,
//    configurable: true
// }

Object.defineProperty(myObject,"b",{    //定义数据描述符
    value:3,
    writable:true,
    configurable:true,
    enumerable:true
})

myObject.b;  //3
复制代码
  • writable控制着改变属性值的能力,writable:false在非严格模式下修改悄无声息失败,严格模式下抛出TypeError
  • configurabletrue时,属性可配置,使用defineProperty(...)修改数据描述符
    • configurable:falsedefineProperty(...)修改数据描述符,无论strict mode与否,都会抛出TypeError,这是一个单向操作不可撤销(例外是在这个情况下,writable可由true变为false
    • configurable:false 阻止的另外一个事情是使用 delete 操作符移除既存属性的能力 ,delete调用无声失败
  • enumerable可枚举性,控制着属性是否能在特定的对象-属性枚举操作中出现

访问器描述符之前先要谈[[Get]][[Put]]操作:

  • [[Get]]操作定义了对象属性访问的行为,简单来说如果当前对象不存在该属性,就会遍历[[Prototype]]链,如果遍历原型链不存在,返回undefined
  • [[Put]]操作定义设置属性值的行为, 简单来说,属性存在访问器描述符setter吗? 调用setter:(writable=false?修改失败或者抛出TypeError:设置属性值)

访问器描述符针对某个属性覆盖[[Get]]、[[Put]]默认操作的一部分,看getter和setter:

//字面量语法定义
var myObject = {
	// 为 `a` 定义 getter
	get a() {
		return this._a_;
	},
	// 为 `a` 定义 setter
	set a(val) {
		this._a_ = val * 2;
	}
};

myObject.a = 2;
myObject.a; // 4

//使用defineProperty定义
Object.defineProperty(myObject,"b",{
    get:function(){return this.a},
    enumerable:true
})
复制代码

注意:访问器描述符不能和writablevalue一起设置,会报错Uncaught TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, #<Object>

应用
  • 将属性或对象设置为不可改变(p.s. 创建的是浅不可变性,如果对象拥有对其他对象的引用,如数组、对象、函数等 ,那个对象的内容不会受影响,依然保持可变),技术上操作数据操作符

    • 设置一个对象属性为常量(不能被改变、重定义、删除):defineProperty(...)设置属性writable:falseconfigurable:false
    • 防止一个对象添加新的属性:Object.preventExtensions(...)
    • 对象不可添加新的属性,对象所有属性不能重定义、不能删除(属性值可以更改):Object.seal(...),相当于Object.preventExtensions(...)+所有对象属性configurable:false
    • 对象不可添加新属性,对象所有属性设置为常量:Object.freeze(...),相当于Object.preventExtensions(...)+所有对象属性configurable:falsewritable:false
  • 在属性访问时得到defined有两种情况,一种属性不存在返回undefiend,另一种属性明确存储值undefined,那么该如何区分?引入属性存在性判断的问题。看个例子:

    var myObject = {
    	a: 2
    };
    
    ("a" in myObject);				// true
    ("b" in myObject);				// false
    
    myObject.hasOwnProperty( "a" );	// true
    myObject.hasOwnProperty( "b" );	// false
    复制代码

    in操作符会检查[[Prototype]]链,而hasOwnProperty(...)只会检查当前对象。你能猜到4 in [2,4,6]的结果是什么吗?

  • 属性枚举性判断。看个例子:

    var a = {
      name: "Harden"
    };
    
    var b = Object.create(a);
    
    b.age = 18;
    
    Object.defineProperty(b, "hobby", {
      value: "basketball",
      enumerable: false
    });
    
    for (pro in b) {
      console.log(b[pro]); //Harden,18
    }
    
    console.log(b.propertyIsEnumerable("name"));	//fasle  当前对象不存在
    console.log(b.propertyIsEnumerable("age"));		//true
    console.log(b.propertyIsEnumerable("hobby"));	//false  属性不可枚举 
    
    console.log(Object.keys(b));	//["age"]
    console.log(Object.getOwnPropertyNames(b));		//["age","hobby"]
    复制代码

    总结一下和枚举型属性相关的几个方法:

    1. for..in是遍历对象可枚举属性,会检查原型链
    2. Object.keys返回给定对象自身可枚举属性组成的数组,不检查原型链
    3. propertyIsEnumerable(...)判断对象自身是否存在给定属性,不检查原型链

    另外Object.getOwnPropertyNames()方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。

  • for..of循环迭代值,要求被迭代的东西提供一个迭代器对象。先看一下数组:

    数组使用for..of迭代是ok的,因为数组拥有内建的@@iterator。 使用Symbol.iterator取得数组的一个内建对象@@iterator@@iterator是一个函数,函数运行返回一个迭代器对象,这样就可以it.next()迭代。

    var a = [1, 2, 3, 4];
    for (value of a) {
      console.log(value);	1,2,3,4
    }
    
    //手动使用@@iterator
    var b = [1,2,3];
    var it = b[Symbol.iterator]();
    console.log(it.next());		//{ value:1, done:false }
    console.log(it.next());		//{ value:2, done:false }
    console.log(it.next());		//{ value:3, done:false }
    console.log(it.next());		//{ done:true }
    复制代码

    再看对象:

    对象没有内建的@@iterator,所以抛出TypeError了。

    var myObject = {
      a: 2,
      b: 3
    };
    
    for (value of myObject) {
      console.log(value);
    }	//oops! Uncaught TypeError: myObj is not iterable
    复制代码

    但是我们也可以自定义迭代器:

    Object.defineProperty(myObject, Symbol.iterator, {
      enumerable: false,
      writable: false,
      configurable: true,
      value: function() {
        var o = this;
        var idx = 0;
        var ks = Object.keys(o);
        return {
          next: function() {
            let value = o[ks[idx++]],
              done = idx > ks.length;
            return { value, done };
          }
        };
      }
    });
    
    //手动迭代
    var it = myObject[Symbol.iterator]();
    console.log(it.next());
    console.log(it.next());
    console.log(it.next());
    
    for (value of myObject) {
      console.log(value);
    }
    复制代码

    自定义迭代器存在一定可操作空间,想想你可以设置迭代返回的结果,是不是很赤激!自己想去吧。

补充:

对象属性遍历的方法:

  • Object.entries(...):对象自身的可枚举属性,返回键值对的数组,demo如下:

    var objF = {
      name: "Zhong"
    }
    
    var objC = {
      age: 22,
      hobby: "basketball"
    }
    
    Object.setPrototypeOf(objC, objF);
    
    Object.entries(objC);	//[["age",22],["hobby","basketball"]]
    复制代码
  • Object.keys :对象自身的可枚举属性,返回键的数组,demo如下:

    Object.keys(objC);	//["age","hobby"]
    复制代码
  • Object.getOwnPropertyNames:对象自身属性,返回键的数组;

  • Reflect.ownKeys(...):对象自身属性,返回键的数组;

Reflect.ownKeys 方法返回一个由目标对象自身的属性键组成的数组。它的返回值等同于Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))。

​ 只有Reflect.ownKeys会得到Symbol类型的属性!

  • Object.values(...):对象自身的可枚举属性,返回值的数组,demo如下:

    Object.values(objC); 	//[22,"basketball"]
    复制代码
  • for..in:检测原型链的可枚举属性,返回键

对应的顺序规则:

  1. 如果属性名类型转为string后是整数,那么顺序是按照key从小到大排序 ;如果属性名是其他的string或者symbol类型,那么顺序是按照属性被创建的时间。demo如下:

    var obj1 = {
      2:2,
      10:10,	//整数
      "8":8,	//字符串的值是整数
      6:6
    }	//Object {2: 2, 6: 6, 8: 8, 10: 10}
    
    var obj2 = {
      name:"Harden",
      age:22,
      hobby:"basketball"
    }	//Object {name: "Harden", age: 22, hobby: "basketball"}
    复制代码
  2. 如果出现混合,顺序分别是"整数",stringsymbol。demo如下:

    var obj3 = {
      name:"Harden",
      5:5,
      age:22,
      1:1,
      hobby:"basketball",
      1.2:1.2	//这是个特例,它不是整数
    }	//Object {1: 1, 5: 5, name: "Harden", age: 22, hobby: "basketball", 1.2: 1.2}
    复制代码

End

文章为个人总结,不妥之处还请雅正。

转载请注明出处。

参考文献:

你不懂JS:this与对象原型 第三章:对象

5分钟彻底理解Object,keys

The "for...in" loop

转载于:https://juejin.im/post/5b6a8b13f265da0fa21aab66

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值