1 构造函数和原型
1.1 概述
在典型的 OOP 的语言中(如 Java),都存在类的概念,类就是对象的模板,对象就是类的实例,但在 ES6之前, JS 中并没用引入类的概念。
ES6, 全称 ECMAScript 6.0 ,2015.06 发版。但是目前浏览器的 JavaScript 是 ES5 版本,大多数高版本的浏 览器也支持 ES6,不过只实现了 ES6 的部分特性和功能。
在 ES6之前 ,对象不是基于类创建的,而是用一种称为构建函数的特殊函数来定义对象和它们的特征。
创建对象可以通过以下三种方式:
- 对象字面量
- new Object()
- 自定义构造函数
ES6之后可以通过类创建对象。
// 1. 利用 new Object() 创建对象var obj1 = new Object();// 2. 利用 对象字面量创建对象var obj2 = {};// 3. 利用构造函数创建对象function Star(username,age) {this.uname = username;this.age = age;this.sing = function () {console.log('明星会唱歌');}}//实例化对象let xzq = new Star('薛志强',30);let mby = new Star('毛不易',20);console.log(xzq);xzq.sing();mby.sing();// 目前为止,四种创建对象的方式.第四种就是通过类创建对象.
1.2 构造函数
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 一起使用。我 们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
在 JS 中,使用构造函数时要注意以下两点:
- 构造函数用于创建某一类对象,其首字母要大写
- 构造函数要和 new一起使用才有意义
new 在执行时会做四件事情:
① 在内存中创建一个新的空对象。
② 让 this 指向这个新的对象。
③ 执行构造函数里面的代码,给这个新对象添加属性和方法。
④ 返回这个新对象(所以构造函数里面不需要 return )
JavaScript 的构造函数中可以添加一些成员,可以在构造函数本身上添加,也可以在构造函数内部的 this 上添 加。通过这两种方式添加的成员,就分别称为静态成员和实例成员.
- 静态成员:在构造函数本身上添加的成员称为静态成员,只能由构造函数本身来访问
实例成员:在构造函数内部创建的对象成员称为实例成员,只能由实例化的对象来访问
/** 成员分为实例成员和静态成员** 在构造函数内部通过this添加的成员,称为实例成员* 静态成员:在构造函数本身上添加的成员.* 静态成员只能通过构造函数本身来访问.实例成员只能通过实例化对象来访问.* */// 构造函数中的属性和方法我们称为成员, 成员可以添加function Star(uname, age) {this.uname = uname;this.age = age;this.sing = function() {console.log('我会唱歌');}}var ldh = new Star('刘德华', 18);// 静态成员.Star.sex = '男';console.log(ldh.sex);//实例化对象访问不到静态成员.console.log(Star.sex);//静态成员只能通过构造函数自己本身访问console.log(Star.uname);//实例化成员不能通过构造函数本身直接访问.
1.3 构造函数的问题
构造函数方法很好用,但是存在浪费内存的问题。
我们希望所有的对象使用同一个函数,这样就比较节省内存,那么我们要怎样做呢?
1.4 构造函数原型 prototype
构造函数通过原型分配的函数是所有对象所共享的。
JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。注意这个 prototype 就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。
问答?
- 原型是什么 ?
一个对象,我们也称为 prototype 为原型对象。 原型的作用是什么 ?
让所有实例对象共享方法,节省内存。/** 构造函数本身是有一个属性(静态成员):原型.* 这个原型属性是一个对象.所以我们把这个属性称为原型对象.** 一般情况下,公共属性 定义到构造函数里面.公共的方法我们放到构造函数原型对象身上* */// 1. 构造函数的问题.function Star(uname, age) {this.uname = uname;this.age = age;// this.sing = function() {// console.log('明星会唱歌');// }}//在构造函数的原型对象身上追加一些方法Star.prototype.sing = function(){console.log('原型对象的唱歌方法');}let mby = new Star('毛不易',20);let xzq = new Star('薛之谦',30);mby.sing();xzq.sing();console.log(mby.sing === xzq.sing );//true 证明使用的是同一份内存地址中的sing函数.节省了内存空间// console.dir(Star);// console.dir(Star.prototype);
1.5 对象原型 proto
对象都会有一个属性__proto__指向构造函数的 prototype 原型对象,之所以实例对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有__proto__原型的存在。
__proto__对象原型和原型对象 prototype 是等价的__proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性, 因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype

/** 实例化的任何一个对象身上都有一个原型---->对象的原型属性--->对象原型.** 构造函数有一个属性叫原型--->这个原型属性是一个对象---->原型对象.** 对象调用方法的时候,是有一个对象查找规则的.对象自身上是否有该方法.如果有,就执行这个对象身上的方法.* 如果没有,因为有__proto__的存在.就去构造函数的原型对象protoType身上去查找该方法.** */function Star(uname, age) {this.uname = uname;this.age = age;// this.sing = function () {// console.log('对象自身上的XXXXX的方法');// }}Star.prototype.sing = function() {console.log('原型对象身上的:我会唱歌');}let mby = new Star('毛不易',20);mby.sing();console.log(mby);// console.log(mby.__proto__ === Star.prototype);//对象原型和原型对象是等价的.
1.6 constructor 构造函数
对象原型(__proto__)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指回构造函数本身。
constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。
一般情况下,对象的方法都在构造函数的原型对象中设置。如果有多个对象的方法,我们可以给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了。 此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。
采用对象赋值成员后,修改了constructor的指向问题,原因是因为我们存放成员的对象里面没有constructor属性了。但是其中的方法,还是可以被实例对象调用。
/** 原型中的constructor的意义就是指回构造函数本身.** 原型对象中有一个属性:构造函数.其意义就是指向哪一个构造函数产生的该原型对象** 对象原生中也有一个属性:构造函数.其意义是指向哪一个构造函数产生的该对象.* */function Star(uname, age) {this.uname = uname;this.age = age;}// Star.prototype.sing = function(){// console.log('明星会唱歌');// }// Star.prototype.dance = function(){// console.log('明星会跳舞');// }// Star.prototype.coding = function(){// console.log('明星会写代码');// }//下面这种方式并不是往原型对象身上追加方法.而是通过一个对象赋值给原型对象.原型对象的地址值,指向,发生了改变.Star.prototype = {//有些情况下,我们可以手动利用constructor 指回构造函数本身constructor : Star,sing: function () {console.log('明星会唱歌');},coding: function () {console.log('明星会写代码');}}let mby = new Star('毛不易', 20);console.log(mby.__proto__.constructor);console.log(Star.prototype.constructor);console.log(mby.__proto__.constructor === Star.prototype.constructor);//true/** 构造函数---->prototype---->原型对象* 原型对象---->constructor------>构造函数** 对象原型----> 原型对象----> constructor------>构造函数* */
1.7 构造函数、实例、原型对象三者之间的关系
](https://www.aliyundrive.com/s/cBAyCLefzou)
1.8 原型链

原型链案例代码:
function Star(uname, age) {this.uname = uname;this.age = age;}Star.prototype.sing = function () {console.log('明星会唱歌');}let mby = new Star('毛不易', 20);// 1:只要是对象,就有对象原型__proto__ 指向的是构造函数的原型对象.console.log(mby.__proto__);console.log(Star.prototype.__proto__);//测试构造函数的原型对象的对象原型 指向的是否是 Object的 构造函数的原型对象console.log(Star.prototype.__proto__ === Object.prototype);// 2:系统的Object的构造函数的原型对象 是一个对象. 对象就会有对象原型. object的构造函数的原型对象的对象原型指向的是nullconsole.log( Object.prototype.__proto__);//null
1.9 JavaScript 的成员查找机制(规则)
① 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该成员。
② 如果没有就查找它的原型(也就是 __proto 指向的 prototype 原型对象)。
③ 如果还没有就查找原型对象的原型(Object的原型对象)。
④ 依此类推一直找到 Object 为止(null)。
⑤ proto 对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。
成员查找机制代码:
function Star(uname, age) {this.uname = uname;this.age = age;}Star.prototype.sing = function () {console.log('明星会唱歌');}// Star.prototype.sex = '女';// Object.prototype.sex = '男女';let mby = new Star('毛不易', 20);// mby.sex = '男';console.log(mby.sex);// 如果object的原型对象上有某个成员,而实例对象自己没有,实例对象的构造函数的原型对象也没有.能使用object的原型对象的该成员吗?当然可以,console.log(mby);console.log(Star.prototype);console.log(Object.prototype);//有一个toString成员方法console.log('----------------');console.log(mby.toString());
1.10 原型对象this指向
构造函数中的this 指向我们实例对象.
原型对象里面放的是方法, 这个方法里面的this 指向的是 这个方法的调用者。
因为一般情况下都是实例对象调用方法,所以当某个实例调用该方法的时候,this指向的就是实例对象。
function Star(uname, age) {this.uname = uname;this.age = age;}let that ;Star.prototype.sing = function () {console.log('明星会唱歌');that = this;}let mby = new Star('毛不易', 20);// 1:在构造函数中的this指向的是实例对象// 2:在原型对象函数中的this.指向的是调用者.// mby.sing();// console.log(mby === that);// Star.prototype.sing();// console.log(that === Star.prototype);// 原型对象中的方法.其实是为了实例调用服务的.所以一般情况下,不会通过原型对象调用方法,而是会通过实例对象调用方法.
1.11 扩展内置对象
可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能。
注意:数组和字符串内置对象不能给原型对象覆盖操作 Array.prototype = {} ,只能是 Array.prototype.xxx = function(){} 的方式。
/** 原型对象的应用之扩展内置对象方法* */Array.prototype.sum=function(){let temp =0;for (let i = 0; i < this.length; i++) {temp += this[i];}return temp;}// 可以追加方法,但是不允许覆盖系统自带的方法// Array.prototype = {// sum : function () {// return 111;// }// }console.log(Array.prototype);let arr = [ 1,2,3,4];console.log(arr.sum());
2 继承
ES6之前并没有给我们提供 extends 继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承。
2.1 call()
调用这个函数, 并且修改函数运行时的 this 指向
fun.call(thisArg, arg1, arg2, ...)
- thisArg :当前调用函数 this 的指向对象
- arg1,arg2:传递的其他参数
10-call方法.mp4/** call(thisArg,arg1,arg2,.....)方法可以帮助我们调用函数。同时还可以修改this指向* 参数:第一个参数是可以不用接收的。默认是用来代表this的。其他的参数,正常传递,正常接受* */function say(num1,num2) {console.log('hello 托尼.....');console.log(this);console.log(num1 + num2)}// say();let pyy ={uname : '彭于晏'}say.call(pyy,10,20);
2.2 借用构造函数继承父类型属性
核心原理: 通过 call() 把父类型的 this 指向子类型的 this ,这样就可以实现子类型继承父类型的属性。
// 借用父构造函数继承属性// 1. 父构造函数function Father(uname, age) {//此处的this是父构造函数的实例对象this.uname = uname;this.age = age;}// 2 .子构造函数function Son(uname, age, address) {//当我调用夫构造函数的时候,让父构造函数的this指向子类的实例对象。Father.call(this, uname, age);//此时的this代表的子构造函数的实例对象this.address = address;}// 实例化子构造函数对象let myb = new Son('毛不易', 30, '东北');console.log(myb);
2.3 借用原型对象继承父类型方法
一般情况下,对象的方法都在构造函数的原型对象中设置,通过构造函数无法继承父类方法。
核心原理:
① 将子类所共享的方法提取出来,让子类的 prototype 原型对象 = new 父类()
② 本质:子类原型对象等于是实例化父类,因为父类实例化之后另外开辟空间,就不会影响原来父类原型对象
③ 将子类的 constructor 从新指向子类的构造函数
// 借用父构造函数继承属性// 1. 父构造函数function Father(uname, age) {// this 指向父构造函数的对象实例this.uname = uname;this.age = age;}Father.prototype.work = function() {console.log('努力工作。。。');};// 2 .子构造函数function Son(uname, age, score) {// this 指向子构造函数的对象实例Father.call(this, uname, age);this.score = score;}//如果子类有方法,应该存在哪里?在子类的原型对象上有方法//子原型对象指向父原型对象方式继承方法:有两个问题:// 1 赋值操作修改了子原型对象。constructor不会指回子构造函数了。如果手动指回子构造,还是有问题,此时的问题就变成了父原定对象的constructor也指向了子构造函数// 2 通过子原型对象追加的方法,父原型对象也会有该方法。原因是子构造和父构造的原定对象其实都变成了父原定对象。// Son.prototype = Father.prototype;//不能用这种方式。Son.prototype = new Father();//当然要记得手动指回子构造函数啦Son.prototype.constructor = Son;//子构造函数的原型对象可不可以有自己的 方法 扩展了功能Son.prototype.exam = function(){console.log('需要考试....');}let son = new Son('小头',5,100);son.work();//确实应该有//实例化一个父类的对象let father = new Father('大头',30);// father.exam();//这不应该有console.log(Son.prototype.constructor);console.log(Father.prototype)
3 类的本质
- class本质还是function.
- 类的所有方法都定义在类的prototype属性上
- 类创建的实例,里面也有 proto 指向类的prototype原型对象
- 所以ES6的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向 对象编程的语法而已。
- 所以ES6的类其实就是语法糖.
语法糖:语法糖就是一种便捷写法. 简单理解, 有两种方法可以实现同样的功能, 但是一种写法更加清晰、方便, 那么这个方法就是语法糖 ```javascript // ES6 之前通过 构造函数+ 原型实现面向对象 编程 // (1) 构造函数有原型对象prototype // (2) 构造函数原型对象prototype 里面有constructor 指向构造函数本身 // (3) 构造函数可以通过原型对象添加方法 // (4) 构造函数创建的实例对象有proto 原型指向 构造函数的原型对象
// ES6 通过 类 实现面向对象编程 class Star {
sing() {console.log('天气色等烟雨');}
}
//1:类的本质其实就是一个函数。我们可以简单理解,类就是构造函数的另一种写法。 //如果复杂理解?其实就是对ES5的组合继承进行封装,class+extends(语法糖) // console.log(typeof Star); // 2:类也有原型对象 类的原型对象也有构造函数属性 也是指向类本身 // console.log(Star.prototype); // console.log(Star.prototype.constructor); // 3: 类里面定义的方法其实就是定义到了原型对象身上。 // 翻车现场:在原型对象身上定义的方法,并没有出现在类体中 Star.prototype.dance = function () {
console.log('一起跳舞')
}
let mby = new Star(); // mby.sing(); // mby.dance();
// 4:类的实例对象 有对象原型 console.log(mby.proto === Star.prototype);
// 5:类的原型对象也是对象有没有对象原型 console.log(Star.prototype.proto === Object.prototype);
[类的本质.mp4](https://www.aliyundrive.com/s/hd6QdctgkiB)<a name="2103ce33"></a>## 4 ES5 中的新增方法<a name="bec5f27b"></a>### 4.1 ES5 新增方法概述ES5 中给我们新增了一些方法,可以很方便的操作数组或者字符串,这些方法主要包括:- 数组方法- 字符串方法- 对象方法<a name="476cf69f"></a>### 4.2 数组方法迭代(遍历)方法:forEach()、map()、filter()、some()、every();<a name="FxIcA"></a>#### forEach方法```javascriptarray.forEach(function(currentValue, index, arr))
- currentValue:数组当前项的值
- index:数组当前项的索引
arr:数组对象本身
/** forEach 迭代(遍历) 数组* forEach(function(currentValue, index, arr){})* 遍历:数组元素挨个拿出来。记得,是所有元素。* */var arr = [1, 2, 3, 7];arr.forEach(function (value, index, array) {// console.log(value);// console.log(index);// console.log(array);console.log(value +'执行了')if (value == 3){console.log(111);return;//只能终止当前的函数后面的代码。并不是终止了forEach的功能}})
filter方法
array.filter(function(currentValue, index, arr))
filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组
- 注意它直接返回一个新数组
- currentValue: 数组当前项的值
- index:数组当前项的索引
arr:数组对象本身
// filter 筛选数组/** 遍历数组同时返回符合条件的元素组成的新数组。---》筛选数组。* */let arr = [12, 17, 4, 88, 3, 7];let newArr = arr.filter(function (value, index) {//需要定义一个规则// return value >= 10;return value % 2 == 0;});console.log(newArr);
some方法
array.some(function(currentValue, index, arr))
some() 方法用于检测数组中的元素是否满足指定条件. 通俗点 查找数组中是否有满足条件的元素
- 注意它返回值是布尔值, 如果查找到这个元素, 就返回true , 如果查找不到就返回false.
- 如果找到第一个满足条件的元素,则终止循环. 不在继续查找.
- currentValue: 数组当前项的值
- index:数组当前项的索引
arr:数组对象本身 ```javascript /*
- some 查找数组中是否有满足条件的元素 返回值Boolean
- some 是遍历数组吗?不是的。是寻找数组中是否有某个元素。 *
- 之所以some会在寻找到符合条件的元素的时候会终止循环,是因为retrun 的是规则。 满足规则,其实就是true的意思喽。
*/ var arr = [10, 0, 30, 20]; //如果20存在这个数组中。我还想知道他的索引是多少
let num; let flag = arr.some(function (value, index, array) { console.log(value + ‘执行执行执行!!!’); return value === 20; // if (value == 0){ // num = index; // // return value;//此时return 的是20 .而能终止some 是需要return true的。 // return true; // } })
console.log(flag);console.log(num);// 1. filter 也是查找满足条件的元素 返回的是一个数组 而且是把所有满足条件的元素返回回来// 2. some 也是查找满足条件的元素是否存在 返回的是一个布尔值 如果查找到第一个满足条件的元素就终止循环
[some方法查找元素是否存在.mp4](https://www.aliyundrive.com/s/C6AvV8Adu9p)<a name="jnN1r"></a>#### 查询商品案例1. 把数据渲染到页面中 (forEach)1. 根据价格显示数据(filter)1. 根据商品名称显示数据```javascript<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>商品查询案例</title><style>table {width: 400px;border: 1px solid #000;border-collapse: collapse;margin: 0 auto;}td,th {border: 1px solid #000;text-align: center;}input {width: 50px;}.search {width: 600px;margin: 20px auto;}</style></head><body><div class="search">按照价格查询: <input type="text" class="start"> - <input type="text" class="end"><button class="search-price">搜索</button>按照商品名称查询: <input type="text" class="product"><button class="search-pro">查询</button></div><table><thead><tr><th>id</th><th>产品名称</th><th>价格</th></tr></thead><tbody></tbody></table></body></html><script>// 利用新增数组方法操作数据let data = [{id: 1,pname: '小米',price: 3999}, {id: 2,pname: 'oppo',price: 999}, {id: 3,pname: '荣耀',price: 1299}, {id: 4,pname: '华为',price: 1999}, {id: 5,pname: '诺基亚',price: 99}];// 1. 获取相应的元素let tbody = document.querySelector('tbody');let start = document.querySelector('.start');let end = document.querySelector('.end');let searchPrice = document.querySelector('.search-price');let product = document.querySelector('.product');let searchPro = document.querySelector('.search-pro');//2:遍历数组data 多个业务都需要渲染数据 所以封装成函数function setDate(currentData){let trs = '';currentData.forEach(function (value, index, array) {// 此时的value是?是一个对象// console.log(value);//准备数据:通过DOM创建元素。然后追加到tbody 还可以使用字符串的方式trs += `<tr><td>${value.id}</td><td>${value.pname}</td><td>${value.price}</td></tr>`;})tbody.innerHTML = trs;}// function setDate(currentData){// tbody.innerHTML = '';// currentData.forEach(function (value, index, array) {// let tr = document.createElement('tr');// tr.innerHTML = `<td>${value.id}</td>// <td>${value.pname}</td>// <td>${value.price}</td>// `;// tbody.appendChild(tr);// })// }//先渲染一下数据setDate(data);// 3:根据价格区间查询商品数据。渲染到表格中。// 查询满足条件的数据。可能有多条或者一条。但是要获取到该数据searchPrice.addEventListener('click',function () {let newDataArr = data.filter(function (value) {// 商品价格 >= start.value && 商品价格 <= end.valuereturn value.price >= start.value && value.price <= end.value;});// console.log(newDataArr);//遍历数组newDataArr 渲染数据setDate(newDataArr);})// 4: 根据商品名称查询// 我们是需要得到一个元素的。filter.但是,这里是查找唯一的元素,是否存在。some.// 但是,我们此处,如果是获取某一个唯一的数据。我认为,使用some更合适。因为效率更好。但是some是不会返回数据。我们只需要解决这个小问题就可以既得到数据,又提高了效率。searchPro.addEventListener('click',function () {let productArr = [];data.some(function (item, index, array) {if (item.pname === product.value){//此时找到的产品的名字是item.pname 当然了,我们需要的是一个对象。而不仅仅是需要一个名字。itemproductArr.push(item);//some什么时候会终止? 是不是找到了某个元素。此时会return true.才会终止循环。// 查到了,需要终止用来提高效率return true;}});//渲染数据setDate(productArr);})</script>
4.3 字符串方法
trim() 方法会从一个字符串的两端删除空白字符。
trim() 方法并不影响原字符串本身,它返回的是一个新的字符串。
str.trim()
startsWith(str) 判断字符串是否以指定的值开头
endsWith(str) 判断字符串是否以指定str 结束。
str.startsWith(arg)str.endsWith(arg)
/** trim 方法去除字符串两侧空格**startsWith(str) 判断字符串是否以指定的值开头*endsWith(str) 判断字符串是否以指定str 结束。* */let str = ' 全责 涛 ';// console.log(str);let newStr = str.trim();// console.log(newStr);let btn = document.querySelector('button');let ipt = document.querySelector('input');btn.onclick = function () {// console.log(ipt.value.trim());// 中间的空格,我们使用正则验证。if(ipt.value.trim().indexOf(' ') === -1 ){console.log('符合条件');}else {console.log('有空格');}}let username = '彭于晏';console.log(username.startsWith('av'));console.log(username.endsWith('哈哈哈'));
4.4 对象方法
- Object.keys() 用于获取对象自身所有的属性
Object.keys(obj)
- 效果类似 for…in
返回一个由属性名组成的数组
// Object.keys()用于获取对象自身所有的属性let obj = {id: 1,name: '全责涛',score: 82,sex: '男'};// for (let objKey in obj) {// console.log(objKey + ':'+ obj[objKey]);// }let keyArr = Object.keys(obj);console.log(keyArr);keyArr.forEach(function (value) {console.log(value + ':' + obj[value]);})
- Object.defineProperty() 定义对象中新属性或修改原有的属性。(了解)
Object.defineProperty(obj, prop, descriptor)
- obj:必需。目标对象
- prop:必需。需定义或修改的属性的名字
- descriptor:必需。目标属性所拥有的特性
Object.defineProperty() 第三个参数 descriptor 说明: 以对象形式 { } 书写
- value: 该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
- writable:当且仅当该属性的 writable 键值为 true 时,属性的值,也就是上面的 value,才能被改变。
- enumerable: 当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。默认为 false。
- configurable: 当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
/** Object.defineProperty(obj, prop, descriptor)定义新属性或修改原有的属性* descriptor:{* value:值,* writable:Boolean,* enumerable:Boolean,* configurable:Boolean* }* */let student = {id: 1,name: '全责涛',score: 82,sex : '男'};//之前给对象追加属性// student.addr = '驻马店';Object.defineProperty(student,'address',{value:'平顶山',enumerable:true});Object.defineProperty(student,'id',{writable:false});student.id = 30;Object.defineProperty(student,'sex',{//如果不写,默认就是不允许被被枚举(遍历)enumerable:false});// console.log(Object.keys(student));Object.defineProperty(student,'score',{value:92,writable:true,enumerable:true,configurable:false});Object.defineProperty(student,'score',{value:102,writable:false,enumerable:false,configurable:false});

