JavaScript学习(第一天)
JavaScript第二天
JavaScript第三天
JavaScript第四天(面向对象,构造函数和原型)高级
JavaScript第五天(函数,this,严格模式,高阶函数,闭包,递归,正则,ES6)高级
面向对象,构造函数和原型
1 JavaScript面向对象
1.1 面向对象编程介绍
1.1.1两大编程思想
- 面向过程
- 面向对象
1.1.2 面向过程编程POP(Process-oriented programming)
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了。
面向过程,就是按照我们分析好了的步骤,按照步骤解决问题
1.1.3 面向对象编程OOP(Object-oriented programming)
面向对象就是把事物分解成为一个个对象,然后由对象之间分工与合作。
面向对象是以对象功能来划分问题,不是步骤
在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工。
面向对象编程具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型软件项目。
面向对象的特性:
- 封装性
- 继承性
- 多态性
1.1.41.4面向过程和面向对象的对比
面向过程
- 优点︰性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用的面向过程编程。
- 缺点:没有面向对象易维护、易复用、易扩展
面向对象
- 优点∶易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
- 缺点︰性能比面向过程低
1.2 ES6 中的类和对象
面向对象
面向对象更贴近我们的实际生活,可以使用面向对象描述现实世界事物.但是事物分为具体的事物和抽象的事物
手机 抽象的(泛指的)
具体的(特指的)
面向对象的思维特点:
1.抽取(抽象)对象共用的属性和行为组织(封装)成一个类(模板)
2.对类进行实例化,获取类的对象
面向对象编程我们考虑的是有哪些对象,按照面向对象的思维特点不断的创建对象使用对象指挥对象做事情.
1.2.1对象
现实生活中︰万物皆对象,对象是一个具体的事物
,看得见摸得着的实物。例如,一本书一辆汽车、一个人
可以是“对象”,一个数据库、一张网页、一个与远程服务器的连接也可以是“对象”。
在JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,
例如字符串、数值、数组、函数等。
对象是由属性和方法
组成的:
- 属性:事物的
特征
,在对象中用属性
来表示(常用名词) - 方法:事物的
行为
,在对象中用方法
来表示(常用动词)
1.2.2 类class
在ES6中新增加了类的概念,可以使用class关键字声明一个类,之后以这个类来实例化对象。
类
抽象了对象的公共部分,它泛指某一大类(class )
对象
特指某一个,通过类实例化一个具体的对象
面向对象的思维特点:
1.抽取(抽象)对象共用的属性和行为组织(封装)成一个类
(模板)
2.对类进行实例化,获取类的对象
1.2.3 创建类
语法:
class name{
//class body
}
创建实例:
var xx=new name();
注意:类必须使用new实例化对象
<script>
// 1.创建类class创建一个明星类
class Star {//Star后面没有小括号
constructor(uname, age) {
this.uname = uname;
this.age = age;
}
}
// 2.利用类创建对象new
var ldh = new Star('刘德华', 18);//Star后面有小括号
var zxy = new Star('张学友', 20);
console.log(ldh);
console.log(zxy);
//(1)通过class 关键字创建类,类名我们还是习惯性定义首字母大写
//(2)类里面有个constructor函数,可以接受传递过来的参数,同时返回实例对象
//(3) constructor函数只要new生成实例时, 就会自动调用这个函数,如果我们不写这个函数, 类也会自动生成这个函数
//(4)生成实例new不能省略
//(5)最后注意语法规范,创建类 类名后面不要加小括号,生成实例类名后面加小括号,构造函数不需要加function
</script>
1.2.4 类添加方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
// 1.创建类class创建一个明星类
class Star {
// 类的共有属性放到constructor 里面
constructor(uname, age) {
this.uname = uname;
this.age = age;
}//这里不需要添加逗号
sing(song){
// console.log('我唱歌');
console.log(this.uname + song);
}
}
// 2.利用类创建对象new
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 20);
console.log(ldh);
console.log(zxy);
// (1)我们类里面的所有函数不需要写function
// (2)多个函数方法之间不需要添加逗号分隔
ldh.sing('冰雨');//方法传递参数
zxy.sing('李香兰');
</script>
</head>
<body>
</body>
</html>
1.3 类的继承
1.3.1 继承(extends关键字)
子类可以继承父类的一些属性和方法。
语法:
class Father{//父类
}
class Son extends Father {//子类继承父类
}
<script>
// 1.类的继承
class Father {
constructor() {
}
money() {
console.log(100);
}
}
class Son extends Father {
}
var son = new Son();
son.money();
</script>
1.3.2 super关键字调用父类中的构造函数
super关键字用于访问和调用对象父类上的函数,可以调用父类的构造函数,也可以调用父类的普通函数
调用了父类中的构造函数,实现子类的数据传递给父类,父类的一些方法就可以使用这些数据了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
// 1.类的继承
class Father {
constructor(x,y) {
this.x=x;
this.y=y;
}
sum() {
// console.log(100);
console.log(this.x+this.y);
}
}
class Son extends Father {
constructor(x,y) {
super(x,y);//调用了父类中的构造函数
}
}
var son = new Son(1,2);
var son2 = new Son(11,2);
son.sum();
son2.sum();
</script>
</head>
<body>
</body>
</html>
1.3.3 super关键字调用父类中的普通函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
class Father {
say() {
return '我是爸爸';
}
}
class Son extends Father {
say() {
// console.1og( '我是儿子');
console.log(super.say() + '的儿子');
// super.say() 就是调用父类中的普通函数say()
}
}
var son = new Son();
son.say();
//继承中的属性或者方法查找原则:就近原则
// 1.继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
// 2.继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)
</script>
</head>
<body>
</body>
</html>
1.3.4 子类继承父类的方法
利用super调用父类的构造函数
super必须在子类this之前调用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
//父类有加法方法
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
sum() {
console.log(this.x + this.y);
}
}
//子类继承父类加法方法同时扩展减法方法
class Son extends Father {
constructor(x, y) {
//利用super调用父类的构造函数
//super必须在子类this之前调用
super(x, y);
this.x = x;
this.y = y;
}
subtract() {
console.log(this.x - this.y);
}
}
var son = new Son(5, 3);
son.subtract();//2
son.sum();//8
</script>
</head>
<body>
</body>
</html>
1.3.5 ES6中的类和对象
三个注意点:
1.在ES6中类没有变量提升,所以必须先定义类,才能通过类实例化对象
2.类里面的共有属性和方法一定要加this使用.
3.类里面的this指向问题.
- constructor 里面的this指向实例对象,方法里面的this指向这个方法的调用者
<!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>Document</title>
</head>
<body>
<button>点击</button>
<script>
var that;
var _that;
class Star {
constructor(uname, age) {
// constructor 里面的this 指向的是 创建的实例对象
that = this;
console.log(this);
this.uname = uname;
this.age = age;
// this.sing();
this.btn = document.querySelector('button');
this.btn.onclick = this.sing;
}
sing() {
// 这个sing方法里面的this 指向的是 btn 这个按钮,因为这个按钮调用了这个函数
console.log(this);
console.log(that.uname); // that里面存储的是constructor里面的this
}
dance() {
// 这个dance里面的this 指向的是实例对象 ldh 因为ldh 调用了这个函数
_that = this;
console.log(this);
}
}
var ldh = new Star('刘德华');
console.log(that === ldh);
ldh.dance();
console.log(_that === ldh);
// 1. 在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象
// 2. 类里面的共有的属性和方法一定要加this使用.
</script>
</body>
</html
1.4 面向对象案例
面向对象版tab栏切换
功能需求:
1.点击tab栏,可以切换效果.
2.点击+号,可以添加tab项和内容项.
3.点击×号,可以删除当前的tab项和内容项
4.双击tab项文字或者内容项文字,可以修改里面的文字内容.
抽象对象: Tab对象
1.该对象具有切换功能
2.该对象具有添加功能
3.该对象具有删除功能
4.该对象具有修改功能
2 构造函数和原型
2.1 构造函数和原型
2.1.1概述
在典型的OOP的语言中(如Java ),都存在类的概念,类就是对象的模板,对象就是类的实例,但在ES6之前,JS中并没用引入类的概念。
ES6,全称ECMAScript 6.0,2015.06发版。但是目前浏览器的JavaScript是ES5版本,大多数高版本的浏览器也支持ES6,不过只实现了ES6的部分特性和功能。
在ES6之前,对象不是基于类创建的,而是用一种称为构建函数的特殊函数来定义对象和它们的特征。
创建对象可以通过以下三种方式∶
1.对象字面量
2.new Object()
3.自定义构造函数
2.1.2构造函数
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
在JS中,使用构造函数时要注意以下两点:
1.构造函数用于创建某一类对象,其首字母要大写
2.构造函数要和new一起使用才有意义
new在执行时会做四件事情:
- 在内存中创建一个新的空对象。
- 让this指向这个新的对象。
- 执行构造函数里面的代码,给这个新对象添加属性和方法。
- 返回这个新对象(所以构造函数里面不需要return ).
1、实例成员(通过this添加的)
通过this添加的
实例成员就是构造函数内部通过this添加的成员uname age sing就是实例成员
实例成员只能通过实例化的对象来访问
例如:
this.uname = uname;
this.age = age;
this.sing = function () {
console.log('我会唱歌');
}
//1.利用new object()创建对象
var obj1 = new Object();
//2.利用对象字面量创建对象
var obj2 = {};
// 3.利用构造函数创建对象
//构造函数中的属性和方法我们称为成员,成员可以添加
function Star(uname, age) {
this.uname = uname;
this.age = age;
this.sing = function () {
console.log('我会唱歌');
}
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友',19);
console.log(ldh);
//构造函数中的属性和方法我们称为成员,成员可以添加
//实例成员就是构造函数内部通过this添加的成员uname age sing就是实例成员
//实例成员只能通过实例化的对象来访问
console.log(1dh.uname);
ldh.sing();
//实例成员只能通过实例化的对象来访问
//console.log(Star.uname); 错误,不可以通过构造函数来访问实例成员
2、静态成员(在构造函数本身上添加的成员就是静态成员)
静态成员只能通过构造函数来访问
Star.sex =‘男’;
// 2.静态成员在构造函数本身上添加的成员sex就是静态成员
Star.sex ='男';
//静态成员只能通过构造函数来访问
console.log(Star.sex);
//console.log(ldh.sex);//不能通过对象来访问
2.1.3构造函数的问题
构造函数方法很好用,但是存在浪费内存的问题。
2.1.4构造函数原型prototype
构造函数通过原型分配的函数是所有对象所共享
的。
JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象。注意这个prototype就是一个对象
,这个对象的所有属性和方法,都会被构造函数所拥有。
我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。
一般情况下,我们的公共属性定义到构造函数里面,公共的方法我们放到原型对象身上
Star.prototype.sing = function() {
console.log('我会唱歌');
}
function Star(uname, age) {
this.uname = uname;
this.age = age;
// this.sing = function () {
// console.log('我会唱歌');
// }
}
Star.prototype.sing = function() {
console.log('我会唱歌');
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 19);
console.log(ldh.sing);
console.log(zxy.sing);
console.log(ldh.sing == zxy.sing);//true
2.1.5对象原型__proto__
对象都会有一个属性__proto__指向构造函数的prototype原型对象,
之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__
原型的存在。
对象身上系统自己添加一个__proto__
指向我们构造函数的原型对象。
- __proto__对象原型和原型对象prototype是等价的
- proto_对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象prototype
console.log(ldh._proto__=== Star.prototype);
//方法的查找规则:首先先看ldh对象身上是否有sing方法,如果有就执行这个对象上的sing
//如果么有sing 这个方法,因为有_proto__的存在,就去构造函数原型对象prototype身上去查找
sing这个方法
2.1.6 constructor 构造函数
1、对象原型(__proto__
)和构造函数( prototype )原型对象里面都有一个属性constructor属性
,constructor我们称为构造函数,因为它指回构造函数本身。
console.log(ldh.__proto__.constructor);
console.log(Star.prototype.constructor);
他们都指向构造函数本身,接下来的的js代码
function Star(uname, age) {//构造函数
this.uname = uname;
this.age = age;
}
Star.prototype.sing = function() {
console.log('我会唱歌');
}
2、很多情况下,我们需要手动的利用constructor这个属性指回原来的构造函数
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
// 很多情况下,我们需要手动的利用constructor这个属性指回原来的构造函数,这个是赋值将原来constructor给覆盖掉了,就没有办法指回原来的构造函数了
Star.prototype = {
//如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
constructor: Star,
sing: function(){
console.log('我会唱歌');
},
movie: function(){
console.log('我会拍电影');
}
}
console.log(ldh.__proto__.constructor);
console.log(Star.prototype.constructor);
2.1.7 构造函数,实例,原型对象三者之间的关系
2.1.8 原型链
2.1.9JavaScript的成员查找机制(规则)
- 当访问一个对象的属性(包括方法)时,首先查找这个
对象自身有没有该属性
。如果有就输出,就近原则 - 如果没有就查找它的原型(也就是
__proto__
指向的(构造函数的)prototype原型对象
)。 - 如果还没有就查找原型对象的原型(
Object的原型对象
)。 - 依此类推一直找到Object为止(
null
) . __proto__
对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。
2.1.10原型对象this指向
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
var that;
star.prototype.sing = function () {
console.log('我会唱歌');
that = this;
}
var ldh = new Star('刘德华', 18);
// 1.在构造函数中,里面this指向的是对象实例ldh
ldh.sing();
console.log(that === ldh);//ture
//2.原型对象函数里面的this 指向的是实例对象 ldh
2.1.11扩展内置对象
可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能。
注意︰数组和字符串内置对象不能给原型对象覆盖操作Array.prototype={},只能是Array.prototype.xxx = function() {}的方式.
// 原型对象的应用扩展内置对象方法
Array.prototype.sum = function () {
var sum = 0;
for (var i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
}
var arr = [1, 2, 3];
console.log(arr.sum());
console.log(Array.prototype);
var arr1 = new Array(11, 22, 33);
console.log(arr1.sum());
2.2 继承
ES6之前并没有给我们提供extends继承。我们可以通过·构造函数+原型对象·模拟实现继承,被称为·组合继承·
2.2.1 call()
调用这个函数并且修改函数运行时的this指向
fun.call (thisArg, arg1, arg2, ...)
- thisArg:当前调用函数this的指向对象
- arg1 , arg2:传递的其他参数
function fn(x,y) {
console.log('我想喝手磨咖啡');
console.log(this);
console.log(x+y);
}
var o = {
name : 'andy'
};
// fn();
//1.call()可以调用函数
// 2.call()可以改变这个函数的this指向,此时的这个函数的this就指向o这个对象
fn.call(o,1,2);
// 把两个数值相加
2.2.2 借用构造函数继承父类型 属性
核心原理:通过call()把父类型的this指向子类型的this,这样就可以实现子类型继承父类型的属性。
//借用父构造函数继承属性
// 1.父构造函数
function Father(uname, age) {
// l this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
// 2 .子构造函数
function Son(uname,age,score) {
// this指向子构造函数的对象实例
Father.call(this, uname, age);
this.score = score ;
}
var son = new Son('刘德华',18,100);
console.log(son);
2.2.3借用原型对象继承父类型方法
//借用父构造函数继承属性
// 1.父构造函数
function Father(uname, age) {
// l this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
Father.prototype.money = function (){
console.log('要挣钱');
}
// 2 .子构造函数
function Son(uname,age,score) {
// this指向子构造函数的对象实例
Father.call(this, uname, age);
this.score = score ;
}
Son.prototype = new Father();
// 如果利用对象的形式修改了原型对象,别忘了利用constructor指回原来的构造函数
Son.prototype.constructor = Son;
Son.prototype.exam = function (){
console.log('要考试');
}
var son = new Son('刘德华',18,100);
console.log(son);
console.log(Father.prototype);
console.log(Son.prototype.constructor);
2.3 ES5的新增方法
2.3.1 ES5新增方法概述
ES5中给我们新增了一些方法,可以很方便的操作数组或者字符串,这些方法主要包括:
- 数组方法
- 字符串方法
- 对象方法
2.3.2数组方法:forEach()
迭代(遍历)方法:forEach()、map()、filter()、some()、every() ;
array.forEach (function (currentvalue,index,arr))
- currentValue:数组当前项的值
- index:数组当前项的索引
- arr:数组对象本身
// forEach迭代(遍历)数组
var arr = [1, 2, 3];
var sum = 0;
arr.forEach(function (value, index, array) {
console.log('每个数组元素' + value);
console.log('每个数组元素的索引号' + index);
console.log('数组本身'+array);
sum += value;
})
console.log(sum);//6
2.3.3数组方法filter()
迭代(遍历)方法:forEach().map()、filter()、some()、every();
array.filter(function (currentvalue,index,arr))
filter()方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组
注意它直接返回一个新数组
- currentValue:数组当前项的值
- index:数组当前项的索引
- arr:数组对象本身
// filter筛选数组
var arr = [12,34,55,88];
var newArr = arr.filter(function(value,index) {
return value >20;
})
console.log(newArr);
2.3.4数组方法some
迭代(遍历)方法:forEach().map()、filter()、some()、every();
array.some (function(currentvalue,index,arr))
- some()方法用于检测数组中的元素是否满足指定条件.通俗点查找数组中是否有满足条件的元素
- 注意它返回值是布尔值如果查找到这个元素,就返回true,如果查找不到就返回false.
- 如果找到第一个满足条件的元素,则终止循环不在继续查找.
- currentValue:数组当前项的值
- index:数组当前项的索引
- arr:数组对象本身
- filter也是查找满足条件的元素返回的是一个数组而且是把所有满足条件的元素返回回来
- some也是查找满足条件的元素是否存在——返回的是一个布尔值如果查找到第一个满足条件的元素就终止循环
forEach、filter、some区别:
只有在some循环里面
return true; //在some里面return 终止迭代
var arr = ['red', 'green', 'blue', ' pink'];
// 1. forEach迭代遍历
// arr.forEach(function (value) {
// if (value =='green') {
// console.log('找到了该元素');
// return true; //在forEach 里面return 不会终止迭代
// }
// console.log(11);
// })
// arr.filter(function (value) {
// if (value =='green') {
// console.log('找到了该元素');
// return true; //在filter里面return 不会终止迭代
// }
// console.log(11);
// })
arr.some(function (value) {
if (value =='green') {
console.log('找到了该元素');
return true; //在some里面return 终止迭代
}
console.log(11);
})
2.3.5字符串方法
trim()方法会从一个字符串的两端删除空白字符。
str. trim()
trim()方法并不影响原字符串本身,它返回的是一个新的字符串。
trim方法去除字符串两侧空格(不会去除中间的空格),并且返回一个新的字符串
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text"> <button> 点击 </button>
<div></div>
<script>
//trim方法去除字符串两侧空格(不会去除中间的空格),并且返回一个新的字符串
var str = " an dy ";
console.log(str);
var str1 = str.trim();
console.log(str1);
var input = document.querySelector(' input');
var btn = document.querySelector(' button');
var div = document.querySelector('div');
btn.onclick = function () {
var str = input.value.trim();
if (str === '') {
alert('请输入内容');
} else {
console.log(str);
console.log(str.length);
// innerHTML 属性设置或返回表格行的开始和结束标签之间的 HTML。
div.innerHTML = str;
}
}
</script>
</body>
</html>
2.3.6 对象方法Object.keys()
-
Object.keys()用于获取对象自身所有的属性名
Object . keys (obj)
●效果类似for…in
●返回一个由属性名组成的数组
<script>
//用于获取对象自身所有的属性
var obj = {
id: 1,
pname: '小米',
price: 1999,
num: 2000
};
var arr = Object.keys(obj);//直接放在了数组里面
console.log(arr);
arr.forEach(function (value) {
console.log(value);
})
</script>
2.3.7 对象方法 Object.defineProperty()
-
Object.defineProperty() 定义对象中新属性或修改原有的属性。
object .defineProperty(obj,prop,descriptor)
●obj :必需。目标对象
●prop :必需。需定义或修改的属性的名字
●descriptor :必需。目标属性所拥有的特性
Object.defineProperty() 第三个参数descriptor说明:以对象形式{}书写
●value: 设置属性的值默认为undefined
●writable:值是否可以重写。true | false 默认为false
●enumerable:目标属性是否可以被枚举。true| false默认为false
●configurable: 目标属性是否可以被删除或是否可以再次修改特性true | false默认为false
writable
// 不允许修改这个属性值
writable:false,
// 允许修改这个属性值
writable:true,
<script>
//用于获取对象自身所有的属性
var obj = {
id: 1,
pname: '小米',
price: 1999,
num: 2000
};
// 1.以前的对象添加和修改属性的方式
// obj.num = 1000;
// obj.price = 99;
// console. log(obj);
// 2. object. defineProperty() 定义新属性或修改原有的属性
Object.defineProperty(obj, 'num', {
value: 1000
});
console.log(obj);
console.log(obj);
Object.defineProperty(obj, 'price', {
value: 9.9
});
console.log(obj);
Object.defineProperty(obj, 'id', {
// 不允许修改这个属性值
writable:false,
});
obj.id = 2;//id还是1
console.log(obj);
</script>
像这种
// 2. object. defineProperty() 定义新属性或修改原有的属性
Object.defineProperty(obj, 'num', {
value: 1000
});
//因为虽然没有指定writable和enumerable值,但是默认是false,都不允许修改和遍历
//想要修改和遍历,就要将指定writable和enumerable值,设置为true
// 如果writable的值为false,不允许修改这个属性值,默认为false
writable: false,
// 如果enumerable的值为false,则不允许遍历,默认为false
enumerable: false
enumerable
如果enumerable的值为false,则不允许遍历,默认为false
configurable
如果configurable的值为false,则不允许删除这个address属性,不允许修改第三个参数里面的特性(value,writable,enumerable,configurable的值),默认为false
Object.defineProperty(obj, 'address', {
value:"山东",
// 如果writable的值为false,不允许修改这个属性值,默认为false
writable: false,
// 如果enumerable的值为false,则不允许遍历,默认为false
enumerable: false,
// 如果configurable的值为false,则不允许删除这个address属性,不允许修改第三个参数里面的特性,默认为false
configurable: false
});