美文网首页
JavaScript中对像的属性

JavaScript中对像的属性

作者: Hill1942 | 来源:发表于2016-09-30 17:31 被阅读0次

在JavaScript中,对象是属性的无序集合,通过对JS属性的理解可以更好地了解JS对象。

属性的构成与分类

在JS中,对像的属性是由名字(key)和一组特性(attribute)构成,其中:

  • key是一个字符串(包括空字符串),
  • attribute是一些与之相关的值,主要是这4个:
    • 值(value),可以是任意的JS值,也可以是一个getter或setter函数(或两者都有)。
      当value为getter或setter时,那么我们称这个属性为“存取器属性(accessor property)”,其他时候则称作“数据属性(data property)”
    • 可写性(writable attribute),表时属性的值是否可以设置
    • 可枚举性(enumerable attribute),表时属性是否可能通过for/in循环返回
    • 可配置性(configurable attribute),表明属性是否可以删除或者修改

实际上,当属性是存取器属性时,是没有可写属性的,只有可枚举和可配置这两个属性。因此,可以这么说,对于一个数据属性,那么它有4个特性:值、可写性、可枚举性和可配置性,对于一个存取器属性,也有4个特性:get、set、可写性和可配置性。

对于存取器属性的值是否可以设置,通过getter和setter来决定的:当只设置了getter函数时,那么这个属性中是只读的;当设置了setter时,那么属性是可写的;两个都设置那么属性是既可读也能写的。

下面是一个具有存取器属性对象的定义:

var person = {
    name: "Kaidi Yang",
    get familyName() {
        return this.name.split(" ")[1];
    },
    set familyName(val) {
        this.name = this.name.split(" ")[0] + " " + val;
    }
};
console.log(person.familyName);  //输出: Yang
person.familyName = "XXXX";
console.log(person.name);   //输出: Kaidi XXXX
var person2 = {
    name: "Kaidi Yang",
    get familyName() {
        return this.name.split(" ")[1];
    }
};
console.log(person2.familyName);  //输出: Yang
person2.familyName = "Wang";
console.log(person2.name);   //输出: Kaidi Yang</pre>  

对于person这个对象中的familyName,它是同时具有getter和setter两个特性的,因此可以设置与改变。而person2这个对象中的familyName,它不具有setter,因此它是一个只读属性,给他设置新的值的话则会失败。不过值得注意的是,给这样的只读属性赋值的操作不会引发错误,这是JS的一个bug,在严格模式中已得到修复。

ES5中也提供一个新的方法Object.defineProperty用来定义对象的属性:

var xxx = {};
Object.defineProperty(xxx, "k", {
    value: 1,
    writable: true,
    enumerable: true,
    configurable: true
});
console.log(xxx.k); // 1
var zzz = {};
Object.defineProperty(zzz, "t", {
    get: function() { return 2 },
    set: function(val) {
        console.log("get new val: " + val);
    },
    enumerable: true,
    configurable: true
});
console.log(zzz.t);  // 2

属性的访问

JS是一门基于原型继承的语言,当在访问对对象的属性时,会从自身开始一直检索整个原型链。

首先,一个JS对象具有“自有属性(own property)”,同时也有一些属性是从原型对象中继承来的。当我们访问一个对象xxx的属性k时,如果xxx中有k这个自有属性,那么,就会返回这个属性;如果对象xxx中没有属性k,那么就会继续在他的原型对象中查找属性k。如果原型对象中也没有,那么就会在这个原型对象的原型对象中查找,直到找到属性k或者查找到一个原型是null。

同样,当我们给对象xxx的属性k赋值时,如果xxx中已经有属性x(自有的)了,那么这个赋值操作就只改变这个自有属性的值;如果xxx中没有这个的自有属性k,那么赋值操作会给xxx加上一个新的自有属性k(如果xxx的的原型对象中有k,那么这个新加上的自有属性会覆盖原来的继承属性)。

上面是两是JS对象属性访问基本的规则,下面指出一些特殊的情况:

  • 不能给只读属性赋值,除非用defineProperty方法把可配置属性变成可写的
  • 在覆盖原型对象中的同名属性时,如果此属性是只读的,那么覆盖会失败。比如下面的代码
console.log(Object.getOwnPropertyNames(navigator))  // []  
navigator.userAgent = "hello";  
console.log(navigator.userAgent);  //Mozilla/5.0 (Windows NT 10.0; WO....

这里我们先输出了navigator的所有自有属性,可以看出是一个空数组,因此可以知道navigator的所有属性值都是从原型对象中继承来的;于是我们就试图在navigator中加入一个userAgent,以期望它能覆盖原型对象中的userAgent;然而从后面的输出中可以看到userAgent没有发生变化,即覆盖失败,这就是因为userAgent中原型对象中是只读的。

  • 在覆盖原型对象中的同名属性时,如果此属性是存取属性且定义了setter方法,那么不会发生覆盖而是当前对象会执行这个setter方法,下面是一个例子
var z = Object.create(zzz);
z.t = 13;  // get new val: 13
console.log(z.t);  // 2 

属性的特性(attribute)

一个属性从构成来看有4个特性,这一点我们可以用过ES5提供的Object.getOwnPropertyDescriptor()方法来查看。比如下面的:

var xxx = {"k": 1};
console.log(Object.getOwnPropertyDescriptor(xxx, "k"));
console.log(Object.getOwnPropertyDescriptor(person, "familyName"));
//下面是输出
{ value: 1, writable: true, enumerable: true, configurable: true }
{ 
    get: [Function: familyName],
    set: [Function: familyName],
    enumerable: true, 
    configurable: true 
}

从上面的输出来看,对于一个新创建的对象,它默认的writable、enumerable和configurable都是true。如果我们想要改变的话,那么通过Object.defineProperty()方法:

Object.defineProperty(person2, "familyName", {
    writable: true,
    enumerable: false,
    configurable: false
});
person2.familyName = "XXXX";
console.log(person2.familyName);  
console.log(Object.getOwnPropertyDescriptor(person2, "familyName"));
//下面是输出
XXXX
{ 
    value: 'XXXX',
    writable: true,
    enumerable: false,
    configurable: false 
}
//试着再定义这个属性
Object.defineProperty(person2, "familyName", {
    writable: true,
    enumerable: true,
    configurable: false
});  //这里报错了

上面的代码,把person这个对象的familyName属性从存取器变成了数据性,enumerable和configurable变成了false。这样,familyName这个属性就变成了一个可写的、不可枚举的、不可配置的数据属性。我们可以设置它为新的值,也能正常访问,不过,却不能用for/in来遍历得到。
但是,当我们再次执行defineProperty,试图改变其枚举性时,JS执行却报错了,这是因为它的configurable已经设置了false。也就是说,一个属性的configurable一旦被设置成false,那么:

  • 它的枚举性和可配性就不能发生变化,
  • 也不能从数据性变成存取性,或从存取性变成数据性,或者修改setter和getter
  • 还不能将要可写性变从false变成true
  • 只能把可写性从true变成false

再回到前面试图修改浏览器userAgent的情况,我们先用getOwnPropertyDescriptor输出navigator.__proto__的中userAgent的情况,可以看到它是一个没有setter函数但是configurable为true的只读属性,因此完全是可以重写getter方法让这返回我们想的数据

Object.defineProperty(navigator.__proto__, "userAgent", {
    get: function() {
        return "hello"
    }
});
console.log(navigator.userAgent); //hello
//或者直接给navigator定义一个数据属性的
Object.defineProperty(navigator, "userAgent", {
    value: "hello"
});
console.log(navigator.userAgent); //hello

相关文章

  • JavaScript中对像的属性

    在JavaScript中,对象是属性的无序集合,通过对JS属性的理解可以更好地了解JS对象。 属性的构成与分类 在...

  • js的prototype

    javascript中的每个对象都有prototype属性,Javascript中对象的prototype属性的解...

  • JavaScript - prototype理解

    Javascript中的每个对象都有prototype属性,Javascript中对象的prototype属性的解...

  • 内置函数

    1、dir() 使用dir()函数可以查看对像内所有属性及方法,在python中任何东西都是对像,一种数据类型,一...

  • JavaScript学习笔记7_对象_2对象属性

    属性检测 JavaScript对象是属性的集合,我们经常需要判断某个属性是否存在于某个对象中。JavaScript...

  • Vue中半场动画的实现

    JavaScript 钩子 可以在属性中声明 JavaScript 钩子 例: 当只用 JavaScript 过渡...

  • vue 中响应式的实现思路

    1. javaScript中对象obj的属性的属性值监听 2. 在javaScript 中实现 发布者/订阅者模...

  • CSS padding margin border属性详解

    W3C组织建议把所有网页上的对像都放在一个盒(box)中,设计师可以通过创建定义来控制这个盒的属性,这些对像包括段...

  • JavaScript中的getter和setter

    JavaScript中的getter和setter 在JavaScript中,可以使用getter属性和sette...

  • javascript中的原型

    javascript 中的原型 javascript 中,每个函数都可以有属性,prototype 就是每个函数都...

网友评论

      本文标题:JavaScript中对像的属性

      本文链接:https://www.haomeiwen.com/subject/yefryttx.html