资讯专栏INFORMATION COLUMN

理解JavaScript的Object.defineProperty()函数

ninefive / 1352人阅读

摘要:理解的函数在进入今天的内容之前我们可以先考虑这么一个场景在你的项目中你有这么一个对象如下所示我们的要求就是你要给添加一个属性当的或者发生变化的时候也要随之变化而且当我们设置了的值的时候那么相应的它的和也随之发生变化那么我们应该怎么做呢如果你

理解JavaScript的Object.defineProperty()函数

在进入今天的内容之前,我们可以先考虑这么一个场景,在你的项目中你有这么一个对象如下所示:

var dreamapple = {
    firstName: "dream",
    lastName: "apple"
};

我们的要求就是你要给dreamapple添加一个fullName属性,当dreamapplefirstName或者lastName发生变化的时候,fullName也要随之变化;而且当我们设置了fullName的值的时候,那么相应的它的firstNamelastName也随之发生变化; 那么我们应该怎么做呢?

如果你使用过Vue.js的话,那么你可以使用它的计算属性来达到这个目的,大概的代码应该是下面这个样子:

// ...
computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + " " + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(" ")
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// ...

如果你使用过Angular 1.x的话,那么你可能会使用$watch来达到这个目的,大概的代码应该是下面这个样子:

// 在控制器中使用 var vm = this;
$scope.$watch("vm.firstName", function() {
    vm.fullName = vm.firstName + " " + vm.lastName;
});
$scope.$watch("vm.lastName", function() {
    vm.fullName = vm.firstName + " " + vm.lastName;
});
$scope.$watch("vm.fullName", function() {
    var names = vm.fullName.trim().split(" ");
    if(2 === names.length) {
        vm.firstName = names[0];
        vm.lastName = names[1];
    }
    else {
        // TODO
    }
});

那我们使用原生的JavaScript可不可以达到这个目的呢?当然可以了;那么我们需要怎么做呢?比较简单的做法就是给这个对象的属性fullName设置一个getter和一个setter,因为这是ES5的特性所以较低版本的浏览器不支持这种特性,但是基本所有的现代浏览器都已经支持.我们只需要写出下面的代码就可以了:

var dreamapple = {
    firstName: "dream",
    lastName: "apple",
    get fullName() {
        return this.firstName + " " + this.lastName;
    },
    set fullName(fullName) {
        var names = fullName.trim().split(" ");
        if(2 === names.length) {
            this.firstName = names[0];
            this.lastName = names[1];
        }
    }
};

dreamapple.firstName = "Dream";
dreamapple.lastName = "Apple";
console.log(dreamapple.fullName); // Dream Apple

dreamapple.fullName = "Jams King";
console.log(dreamapple.firstName); // Jams
console.log(dreamapple.lastName); // King

是不是很方便呢?我们通过给dreamapple这个对象设置了属性fullNamegettersetter方法,就达到了我们想要的那种效果.

当然更好的一种方法就是使用Object.defineProperty()这个函数了,下面我们就来好好的探讨一下这个函数.这个方法的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性,并返回这个对象;我们先来看一下怎么使用这个方法:

Object.defineProperty(obj, prop, descriptor)

其中参数obj表示的是需要定义属性的那个对象,参数prop表示需要被定义或者修改的属性名,参数descriptor就是我们定义的那个属性prop的描述;我们接下来主要讲解这个descriptor.它是一个对象,它有许多的属性,我们接下来来分析这些属性都是干什么用的:

value 该属性对应的值,可以是任何有效的JavaScript值(数值,对象,函数等),默认为undefined.我们可以看下面的一个小例子:

var dream = {};
Object.defineProperty(dream, "name", {
    value: "dreamapple"
});

console.log(dream.name); // dreamapple
dream.name = "apple"; // 修改name属性
console.log(dream.name); // 并不是apple,依旧是dreamapple

从上面的代码中我们可以看到,我们给dream定义了一个新的属性name,然后我们打印出这个属性就是我们预期的那样,得到的是dreamapple;但是,当我们尝试改变这个属性的时候,却发现这个属性并没有改变,还以第一次我们赋给它的值;这是为什么呢?原来,只有当我们这个属性的writable修饰为true时,我们这个属性才可以被修改.

writable 当且仅当仅当该属性的writabletrue时,该属性才能被赋值运算符改变;它的默认值为false.我们来修改一下上面的代码,让属性name可以被修改:

Object.defineProperty(dream, "name", {
    value: "dreamapple",
    writable: true
});

console.log(dream.name); // dreamapple
dream.name = "apple"; // 修改name属性
console.log(dream.name); // apple

我们可以看到,当我们把writable修改为true时,我们就可以修改name属性了.

enumerable 这个特性决定了我们定义的属性是否是可枚举的类型,默认是false;只有我们把它设置为true的时候这个属性才可以使用for(prop in obj)Object.keys()中枚举出来.就像下面这样:

Object.defineProperty(dream, "a", {
    value: 1,
    enumerable: false // 不可枚举
});
Object.defineProperty(dream, "b", {
    value: 2,
    enumerable: true // 可枚举
});

// 只会输出 b
for(prop in dream) {
    console.log(prop);
}

console.log(Object.keys(dream)); // ["b"]

console.log(dream.propertyIsEnumerable("a")); // false
console.log(dream.propertyIsEnumerable("b")); // true

所以当我们想给你个对象添加一个不可枚举的属性的时候,就应该把enumerable设置为false.

configurable 这个特性决定了对象的属性是否可以被删除,以及除writable特性外的其它特性是否可以被修改;并且writable特性值只可以是false我们可以写一个代码示例来演示一下这个特性:

Object.defineProperty(dream, "c", {
    value: 3,
    configurable: false
});
 //throws a TypeError
Object.defineProperty(dream, "c", {
    configurable: true
});
 //throws a TypeError
Object.defineProperty(dream, "c", {
    writable: true
});
 //won"t throws a TypeError
Object.defineProperty(dream, "c", {
    writable: false
});
delete dream.c; // 属性不可以被删除
console.log(dream.c); // 3 

get 一个给属性提供getter的方法,如果没有getter则为undefined;该方法返回值被用作属性值,默认为undefined.

set 一个给属性提供setter的方法,如果没有setter则为undefined;该方法将接受唯一参数,并将该参数的新值分配给该属性,默认为undefined.知道了这些之后我们就可以使用更标准的一种方式去解决我们在文中开头的问题了:

Object.defineProperty(dreamapple, "fullName", {
    enumerable: true,
    get: function () {
        return this.firstName + " " + this.lastName;
    },
    set: function (fullName) {
        var names = fullName.trim().split(" ");
        if (2 === names.length) {
            this.firstName = names[0];
            this.lastName = names[1];
        }
    }
});

还有一点需要注意的是,valueget,set是不可以共存的,就是说你定义了value后就不能够再定义get,set特性了.

好啦,今天的文章就写到这里了,相信大家对于Object.defineProperty(obj, prop, descriptor)这个方法应该掌握了;还有一点需要提及的是其实Vue.js计算属性也是在这个函数的基础上进行的一些改进,详情可以看这里计算属性的奥秘.

如果你对这篇文章有什么意见或者建议可以在这里提出来issues

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

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

相关文章

  • 深入理解JavaScript属性和特性

    摘要:深入理解中的属性和特性中属性和特性是完全不同的两个概念,这里我将根据自己所学,来深入理解中的属性和特性。其中第三个参数描述符对象是对象字面量的方法创建的,里面的属性和属性值实际上保存的是要修改的特性和特性值。 深入理解JavaScript中的属性和特性   JavaScript中属性和特性是完全不同的两个概念,这里我将根据自己所学,来深入理解JavaScript中的属性和特性。   主...

    VPointer 评论0 收藏0
  • Javascript面向对象程序设计_理解对象

    摘要:面向对象的程序设计理解对象前言最近在细读高级程序设计,对于我而言,中文版,书中很多地方翻译的差强人意,所以用自己所理解的,尝试解读下。总结如果英语水平足够好的话,建议看英文原版书籍或者国外大师的博客。 JS面向对象的程序设计_理解对象 前言:最近在细读Javascript高级程序设计,对于我而言,中文版,书中很多地方翻译的差强人意,所以用自己所理解的,尝试解读下。 如有纰漏或错误,会非...

    刘厚水 评论0 收藏0
  • JavaScript基础学习——面向对象部分(属性类型)

    摘要:今天结合高编第六章开始回顾和深入学习面向对象部分包括对象原型原型链继承等部分。二对象的属性类型勾鑫宇,数据属性访问器属性书上讲到属性类型时,只是简单提了一下是为了表示对象的特性,描述了属性的特征,并且在中不能直接访问。 前言 JavaScript发明之始,从技术上来讲就是一门面向对象的语言,但在ES6之前,JS的很多特性和传统的面向对象语言有所不同,比如没有类的概念(ES6有了clas...

    microelec 评论0 收藏0
  • JavaScript深入理解对象方法——Object.defineProperty()

    摘要:返回值被传递给函数的对象。描述该方法允许精确添加或修改对象的属性。描述符必须是两种形式之一不能同时是两者。可以是任何有效的值数值,对象,函数等。该方法返回值被用作属性值。该方法将接受唯一参数,并将该参数的新值分配给该属性。 Object.defineProperties() Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性...

    woshicixide 评论0 收藏0
  • javascript 面向对象版块之对象属性

    摘要:上一篇面向对象版块之理解对象下一篇面向对象版块之定义多个对象属性以及读取属性特性 这是 javascript 面向对象版块的第二篇文章,主要讲解的是对象的属性,首先创建一个对象: var person = { name: Nicholas, age: 29, job: Software Engineer, sayName: function () { conso...

    lolomaco 评论0 收藏0

发表评论

0条评论

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