资讯专栏INFORMATION COLUMN

JavaScript对象详解

fancyLuo / 1475人阅读

摘要:属性描述符升级打怪必备技能对象有自己的属性和方法,对于我们对象的属性来讲,属性还有自己的属性,又称为属性描述符。这个方法接受三个参数,第一个是指定的对象,第二个是指定的对象参数,第三个当然是要修改的属性描述符了。

对象的声明有俩种:

字面量

通过new一个构造函数Object

两者唯一的区别就是,字面量形式,可以一次赋值多个,通过new Object就得一个一个赋值

数据类型

JS种7种数据类型

number

string

undefined

null

boolean

object

symbol(es6新增,也不算新了,es6都多久了...)

简单数据类型

number

string

undefined

null

boolean

复杂基本类型(内置对象,object子类型)

Number

String

Boolean

Object

Function

Array

RegExp

Date

Error

补充:null为什么是基本类型但是依然会出现,typeof null 会返回 object?这是一个语言的bug,原理如下:原理是这样的,不同的对象在底层都表示为二进制,在 JavaScript 中二进制前三位都为 0 的话会被判
断为 object 类型,null 的二进制表示是全 0,自然前三位也是 0,所以执行 typeof 时会返回“object”。

深入对比基本类型和复杂类型的区别,用string和String对比

var str = "hello world";
var strObj = new String("hello String");

str.length                          // 11
strObj.length                    // 12

// typeof判断属于那种简单数据类型
typeof str;                         // "string"
typeof strObj;                     //"object"

// instanceof判断对象实例的鼻祖是哪个复杂类型
str instanceof String         //  false
strObj instanceof String   // true

这段代码可以分析出,str通过字面量形式声明,其数据类型为基本数据类型,并不是对象,strObj 为复杂数据类型String的实例。我们知道str, strObj的数据类型之后,就要抛出一个问题,为什么str并不是对象,它怎么就能调用length呢?这不是一个简单数据类型该有的呀。js向你丢出一个结论,那就是简单数据类型存在一个隐式转换,它会从简单数据类型转换为复杂数据类型。

对象 1. 对象的属性

首先以对象字面量整一个对象

let obj = {
    name: "hello",
    age: 12
}

访问对象的属性可以通过 圆点运算符 类似于 obj.name, 还可以通过属性运算符 obj["name"]访问。

2. 浅拷贝,深拷贝

ES6 定义了Object.assign()方法实现浅复制,该方法第一个参数是目标对象,之后还可以跟一个或多个原对象,他会遍历一个或多个原对象所有可枚举的自有键并把他们复制(使用 = 操作符赋值,所以这种僵硬的赋值操作,不会将原对象的属性的特性复制过来)到目标对象,最后返回目标对象。

3. 属性描述符【升级打怪必备技能】

对象有自己的属性和方法,对于我们js对象的属性来讲,属性还有自己的“属性”,又称为属性描述符。不要小看这个属性描述符,可不得了,通过属性描述符,我们不仅仅可以配置属性是读写权限,甚至通过setter,getter实现我们的数据双向绑定。该介绍一下这位调用属性描述符的内置方法了:Object.defineProperty()。这个方法接受三个参数,第一个是指定的对象,第二个是指定的对象参数,第三个当然是要修改的属性描述符了。当对象中不存在指定的属性,Object.defineProperty()就创建这个属性,当描述符中省略某些字段时,这些字段将使用它们的默认值,拥有布尔值的字段的默认值都是false

所以这也就是说,通过Object.defineProperty()创建的属性,描述符是不可配置的,因为configurable:false
// 直接对一个对象使用原点运算符赋值,属性描述符都是true
var o = {};

o.a = 1;
// 等同于 :
Object.defineProperty(o, "a", {
  value : 1,
  writable : true,
  configurable : true,
  enumerable : true
});


// 另一方面,使用Object.defineProperty()却将属性修饰符关闭了
Object.defineProperty(o, "a", { value : 1 });
// 等同于 :
Object.defineProperty(o, "a", {
  value : 1,
  writable : false,
  configurable : false,
  enumerable : false
});
var obj = {
    name: "hello",
    age: 12
}

Object.defineProperty(obj, "name", {
    value: 2,
    writable: true,
    configurable: true,
    enumerable: true
})

obj.name;       //2 not hello

接下来,我们解释一下属性描述符的四大天王:value, writable, configurable, enumerable

configurable

为啥先要介绍configurable呢,不得不说说他的重要性,这个描述符的值是true/false,它表示属性描述符是否可配置,当值为false时,剩下的描述符都是不可配置的,修改也不会生效,不包括value。简单的说,configurable控制的其他描述符的命运!后面我们讨论的时候,默认configurable为true,不然,讨论也没有意义,根本不会改变嘛

var a = {
    name: "tom",
    age: 22
}

Object.defineProperty(a, "age", {
    enumerable: false,
    writable:false,
    configurable: false,
})
a.age = 12;   // a.age 为12.即使writable 是false,因为configurable是false,所以writable其实还是默认的true

我知道你肯定是不信的,直接上图。Object.getOwnPropertyDescriptor() 与Object.defineProperty()互为一对儿,一个定义,一个查看定义

而且,将configurable修改为false,这是一个单向操作,你想再次修改它的值为true以实现重新配置属性描述符是不可能的,一旦设置false,永远都是false了。听说你还不信,那我再上图

var myObject = {
    a:2
};
myObject.a = 3;
myObject.a; // 3
Object.defineProperty( myObject, "a", {
    value: 4,
    writable: true,
    configurable: false, // 不可配置!
enumerable: true
} );

myObject.a; // 4
myObject.a = 5;
myObject.a; // 5

Object.defineProperty( myObject, "a", {
    value: 6,
    writable: true,
    configurable: true,
    enumerable: true
} ); // TypeError

writable

我们介绍一下控制读写的小伙伴-writable,这个属性描述符控制我们是否可以对属性进行赋值,值为false时,赋值是不管用的。严格模式下,还会抛出错误。至于writable怎么处理属性是否能被赋值,留一个疑问,稍后马上揭晓。

// configurable为true
var myObject = {};

Object.defineProperty( myObject, "a", {
    value: 2,
    writable: false, // 不可写!
    configurable: true,
    enumerable: true
} );

myObject.a = 3;
myObject.a; // 2

Enumerable

这个属性修饰符可能没有那么直观,但是从字面来看,我们应该是能了解到的,这是控制属性是否能出现在对象可枚举属性中。如果将enumerable设置为false,属性就不会出现在可枚举属性中。enumerable定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。

var obj = {
    name: "tom",
    age: 12
}

for(prop in obj) {
    console.log(prop);     // name, age
}

Object.defineProperty(obj, "name", {
    enumerable: false,
})

for(prop in obj) {
    console.log(prop);                //age
}

补充一些可枚举相关的知识,什么叫可枚举属性,就是指那些属性描述符设置为enumerable为true的属性。有很多方法是基于可枚举性的。比如,Object.keys(), in, for in 。对于上面那个例子,就是使用了for in遍历obj对象可枚举属性。

tips: 利用属性修饰符拓展的功能

ES6 实现的常量const
本身JS是没有常量这个概念的,但是我们可以使用writable:false 和 configurable:false,就可以创建一个一次赋值,不能修改的常量

禁止拓展
如果想禁止一个对象添加新属性并且保留已有属性,可以使用Object.preventExtensions(),这样显式赋值就会静默失败,严格模式下,将会抛出TypeError异常

密封,禁止拓展加强版,已有的属性以及属性修饰符都不能改
Object.seal(..) 会创建一个“密封”的对象,这个方法实际上会在一个现有对象上调用
Object.preventExtensions(..) 并把所有现有属性标记为 configurable:false。
所以,密封之后不仅不能添加新属性,也不能重新配置或者删除任何现有属性(虽然可以
修改属性的值)。

冻结,更狠,Object.seal() 加强版,直接修改属性修饰符writable:false,值都不能动了。
Object.freeze(..) 会创建一个冻结对象,这个方法实际上会在一个现有对象上调用
Object.seal(..) 并把所有“数据访问”属性标记为 writable:false,这样就无法修改它们
的值。

4. Getter/Setter

ES5可以使用getter和setter部分改写默认操作,但是只能应用在单个属性上,无法应用在整个对象上,getter,setter都是隐藏函数,前者在获取属性值时自动调用,后者是在设置属性值时自动调用。其实现在,我们可以总结一下前文所述。属性描述符其实分为两种

数据修饰符

访问修饰符

而我们前文一直在讲述数据修饰符,那么什么是访问修饰符呢?
当我们给一个属性定义getter和setter或者两者都有时,这个属性会被定义为”访问描述符“,对于访问描述符而言,JavaScript会忽略它们的value和writable特性,取而代之的是关心set/get(还有configurable和enumerable)特性

实例演示:

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 }]

一言以蔽之,setter/getter无非就是我们在赋值或者读取变量时,预先悄悄调用的一个隐藏方法。
还有一种通过对象文字语法创建的get,set(我也是从《你不知道的js》看到这种用法的,MDN这相关的文档中我并没有看到,学到了),这个不是通过Object.defineProperty()创建的,二者都会在对象中创建一个不包含值的属性,对于这个属性的访问会自动调用一个隐藏函数,它的返回值会被当作属性访问的返回值。

其实可以更细致的观察到,通过对象文字语法创建的变量,他的修饰符按照规则肯定是没有value的,但是其他配置信息是true,这一点很关键

大体上对象的内容就是这么多,后续在做一个补充,MDN实在是前端宝典,看到这里更推荐读者阅读MDN相关文档:https://developer.mozilla.org...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/108314.html

相关文章

  • 【连载】前端个人文章整理-从基础到入门

    摘要:个人前端文章整理从最开始萌生写文章的想法,到着手开始写,再到现在已经一年的时间了,由于工作比较忙,更新缓慢,后面还是会继更新,现将已经写好的文章整理一个目录,方便更多的小伙伴去学习。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 个人前端文章整理 从最开始萌生写文章的想法,到着手...

    madthumb 评论0 收藏0
  • javaScript原型及原型链详解(二)

    摘要:当然这还没完,因为我们还有重要的一步没完成,没错就是上面的第行代码,如果没有这行代码实例中的指针是指向构造函数的,这样显然是不对的,因为正常情况下应该指向它的构造函数,因此我们需要手动更改使重新指向对象。 第一节内容:javaScript原型及原型链详解(二) 第一节中我们介绍了javascript中的原型和原型链,这一节我们来讲利用原型和原型链我们可以做些什么。 普通对象的继承 ...

    widuu 评论0 收藏0
  • javascript对象原型成员详解

    摘要:和的作用一样,区别在于写法语法对象对象作用判断对象是否在对象的原型链上语法对象构造函数作用判断构造函数的属性是否在对象的原型链上,如果在,就返回属性是否可枚举用于检查给定的属性是否能够使用语句。 ## javascript对象原型成员详解 ## ECMAScript 中的对象就是一组数据和功能的集合,对象可以通过 new 操作符后跟要创建的对象名称来...

    zhkai 评论0 收藏0
  • JavaScript深入浅出

    摘要:理解的函数基础要搞好深入浅出原型使用原型模型,虽然这经常被当作缺点提及,但是只要善于运用,其实基于原型的继承模型比传统的类继承还要强大。中文指南基本操作指南二继续熟悉的几对方法,包括,,。商业转载请联系作者获得授权,非商业转载请注明出处。 怎样使用 this 因为本人属于伪前端,因此文中只看懂了 8 成左右,希望能够给大家带来帮助....(据说是阿里的前端妹子写的) this 的值到底...

    blair 评论0 收藏0
  • JavaScript 中 this 的详解

    摘要:作为构造函数调用中没有类,但是可以从构造器中创建对象,并提供了运算符来进行调用该构造器。构造器的外表跟普通函数一样,大部分的函数都可以当做构造器使用。如果构造函数显式的返回一个对象,那么则会指向该对象。 this 的指向 this 是 js 中定义的关键字,它自动定义于每一个函数域内,但是它的指向却让人很迷惑。在实际应用中,this 的指向大致可以分为以下四种情况。 1.作为普通函数调...

    cyrils 评论0 收藏0

发表评论

0条评论

fancyLuo

|高级讲师

TA的文章

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