第一章:构造函数和原型
1.1 概述
- 在典型的OOP的语言中(如Java),都存在类的概念,类就是对象的模板,对象就是类的实例,但是在ES6之前,JS并没有引入类的概念。
- ES6,全称ECMAScript 6.0,2015.06发版。但是目前浏览器的JavaScript是ES5版本,大多数高版本的浏览器也支持ES6,不过只实现了ES6的部分特性和功能。
- 在ES6之前,对象不是基于类创建的,而是用一种称为
构造函数
的特殊函数来定义对象和他们的特征。 创建对象的三种方式:
- 对象字面量。
- new Object()。
- 自定义构造函数。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>利用构造函数创建对象</title>
<script>
/* 利用new Object()创建对象 */
var obj1 = new Object();
obj1.name = '刘德华';
obj1.age = 55;
console.log(obj1.name);
console.log(obj1.age);
/* 利用字面量创建对象 */
var obj2 = {
name: '刘德华',
age: 55,
show: function () {
console.log(this.name + ':' + this.age);
}
}
obj2.show();
/* 利用构造函数创建对象 */
function Person(name, age) {
this.name = name;
this.age = age;
this.show = function () {
console.log(this.name + '~:~' + this.age);
}
}
var person = new Person('刘德华', 55);
person.show();
</script>
</head>
<body>
</body>
</html>
1.2 构造函数
构造函数
是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总和new
一起使用。我们可以将对象中的一些公共的属性和方法抽取出来,然后封装到这个函数里面。- 在JS中,使用构造函数的时候需要注意以下两点:
- 构造函数用于创建某一类的对象,其
首字母要大写
。 - 构造函数要
和new一起使用
才有意义。
- 构造函数用于创建某一类的对象,其
new
在执行的时候会做四件事情:- 在内存中创建一个新的空对象。
- 让this指向这个新的对象。
- 执行构造函数里面的代码,给这个新对象添加属性和方法。
- 返回这个新对象(构造函数里面不需要return)。
1.3 静态成员和实例成员
- JavaScript的构造函数中可以添加一些成员,可以在构造函数本身上添加,也可以在构造函数内部的this上添加。通过这两种方式添加的成员,就分别称为
静态成员
和实例成员
。 静态成员
:在构造函数本身上添加的成员称为静态成员,只能由构造函数本身来访问
。实例成员
:在构造函数内部创建的对象成员称为实例成员,只能由实例化的对象来访问
。示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>静态成员和实例成员</title>
<script>
//构造函数中的水性和方法我们称为成员,成员可以添加
function Person(uname, age) {
this.uname = uname;
this.age = age;
this.sing = function () {
console.log('我会唱歌')
}
}
var ldh = new Person('刘德华', 55);
//实例成员就是构造函数内部通过this添加的成员 uname age sing就是实例成员
//实例成员只能通过实例化的对象来访问
console.log(ldh.uname);
console.log(ldh.age);
ldh.sing();
// console.log(Person.uname); 不可以通过构造函数来访问实例成员
//静态成员,在构造函数本身上添加的成员 sex 就是静态成员
Person.sex = '男';
console.log(Person.sex)
// console.log(ldh.sex) 静态成员只能通过构造函数访问
</script>
</head>
<body>
</body>
</html>
1.4 构造函数的问题
- 构造函数方法很好用,但是
存在浪费内存的问题
。
学过Java的一定要注意,这边不一样!!!
我们希望所有对象使用同一个函数,这样就比较节省内存,那么我们要怎么做?
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>构造函数的问题</title>
<script>
function Person(uname, age) {
this.uname = uname;
this.age = age;
this.sing = function () {
console.log('我会唱歌')
}
}
var ldh = new Person('刘德华', 55);
var zxy = new Person('张学友', 50);
console.log(ldh.sing == zxy.sing); //false
</script>
</head>
<body>
</body>
</html>
1.5 构造函数原型prototype
- 构造函数通过原型分配的函数是所有对象所
共享
的。 - JavaScript中规定,
每一个构造函数都有一个prototype属性
,指向另一个对象。注意,这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。 我们可以将一些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法
。示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>原型对象</title>
<script>
function Person(uname, age) {
this.uname = uname;
this.age = age;
}
Person.prototype.sing = function () {
console.log('我会唱歌');
}
var ldh = new Person('刘德华', 55);
var zxy = new Person('张学友', 50);
ldh.sing();
zxy.sing();
console.log(ldh.sing == zxy.sing); //true
//一般情况下,我们的公共属性定义到构造函数里面,公共的方法放到原型对象上面。
</script>
</head>
<body>
</body>
</html>
1.6 对象原型__proto__
对象都会有一个属性__proto__
,这个属性指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__
原型存在。__proto__
对象原型和原型对象prototype是等价的。__proto__
对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此在实际开发中,不可以使用这个属性,它只是内部指向原型对象prototype。
1.7 constructor构造函数
对象原型(__proto__)
和原型对象(prototype)
里面都有一个属性constructor
属性,constructor我们称之为构造函数,因为它指向构造函数本身。- constructor主要用于记录该对象引用于那个构造函数,它可以让原型对象重新指向原来的构造函数。
一般情况下,对象的方法都是在构造函数的原型对象中设置的
。但是如果对象有多个方法,我们可以给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象constructor就不再指向当前的构造函数了,此时,我们可以在修改后的原型对象中,添加一个constructor指向原来的构造函数。示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>constructor构造函数</title>
<script>
function Person(uname, age) {
this.uname = uname;
this.age = age;
}
//很多情况下,我们需要手动的利用constructor这个属性指回原来的构造函数。
/*Person.prototype.sing = function () {
console.log('我会唱歌');
}
Person.prototype.movie = function () {
console.log('我会演电影');
}*/
Person.prototype = {
/* 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数 */
constructor: Person,
sing: function () {
console.log('我会唱歌');
},
movie: function () {
console.log('我会唱歌');
}
}
var ldh = new Person('刘德华', 55);
var zxy = new Person('张学友', 50);
ldh.sing();
zxy.sing();
console.log(Person.prototype.constructor);
console.log(ldh.__proto__.constructor);
</script>
</head>
<body>
</body>
</html>
1.8 构造函数、实例、原型对象三者之间的关系
1.9 原型链
- 示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>原型链</title>
<script>
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
Star.prototype.sing = function () {
console.log('我会唱歌');
}
var ldh = new Star('刘德华', 55);
// 只要是对象就有__proto__原型属性,指向原型对象
console.log(ldh.__proto__);
console.log(Star.prototype.__proto__ == Object.prototype); // true
// Star原型对象里面的__proto__指的是Object里面的prototype
console.log(Object.prototype.__proto__);//null
</script>
</head>
<body>
</body>
</html>
1.10 Javascript的成员查找机制(规则)
- ①当访问一个对象的属性(包括方法)的时候,首先查找这个
对象本身
有没有该属性。 - ②如果没有就查找它的原型(也就是
__proto__
指向的prototype原型对象)。 - ③如果还没有,就查找原型对象的原型(
Object的原型对象
)。 - ④依次类推,一直找到Object为止(如果还没有,就返回
null
)。 ⑤
__proto__
对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>原型链成员查找规则</title>
<script>
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
Star.prototype.sing = function () {
console.log('我会唱歌');
}
//还找不到返回null
//最后找这个
// Object.prototype.sex = '男';
//再找这个
// Star.prototype.sex = '女';
var ldh = new Star('刘德华', 55);
//先找下面的
// ldh.sex = '男';
console.log(ldh.sex);
</script>
</head>
<body>
</body>
</html>
1.11 原型对象this指向
- 构造函数中的this指向的是实例对象。
原型对象
里面放的是方法,这个方法里面的this指向的是这个方法的调用者,也就是这个实例对象
。示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>原型对象this指向</title>
<script>
function Star(uname, age) {
console.dir(this);
this.uname = uname;
this.age = age;
}
var that;
Star.prototype.sing = function () {
that = this;
console.dir(this);
console.log('我会唱歌');
}
// 在构造函数中,里面的this指向的是对象实例ldh。
var ldh = new Star('刘德华', 55);
ldh.sing();
// 原型对象函数里面的this,指的是对象实例ldh。
console.log(that == ldh); //true
</script>
</head>
<body>
</body>
</html>
1.12 扩展内置对象
- 可以通过原型对象,对原来的内置对象进行扩展自定义的方法,比如给数组增加自定义求偶数和的功能。
注意:数组和字符串内置对象不能给原型对象覆盖操作 Array.prototype = {} ,只能是 Array.prototype.xxx = function(){} 的方式。
- 示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>扩展内置对象方法</title>
<script>
Array.prototype.evenSum = function () {
var sum = 0;
for (var i = 0; i <= this.length; i++) {
if (this[i] % 2 == 0) {
sum += this[i];
}
}
return sum;
}
var arr = [1, 2, 3, 4, 5];
var evenSum = arr.evenSum();
console.log(evenSum);
</script>
</head>
<body>
</body>
</html>
第二章:继承
2.1 概述
- ES6之前并没有给我们提供
extends
继承。我们可以通过构造函数+原型对象
模拟实现继承,被称为组合继承
。
2.2 call()
- 语法:
/*
thisArg:当前调用函数this的指向对象
arg1,arg2:传递的其他参数
*/
fun.call(thisArg,arg1,arg2,...)
调用这个函数,并且修改函数运行时的this的指向。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>call方法</title>
<script>
function fn(x, y) {
console.log('我想喝手磨咖啡...');
console.log(this);
console.log(x);
console.log(y);
}
var o = {
name: 'tom'
};
// fn();
// 1. call()方法:可以调用函数
// fn.call(); 原来的this指向的是window
// 2. call()方法可以改变函数的this指向
fn.call(o, 1, 2); // 此时这个函数的this,就指向了o这个对象
</script>
</head>
<body>
</body>
</html>
2.2 借用构造函数继承父类型属性
核心原理:通过call()将父类型的this指向子类型的this,这样就可以实现子类型继承父类型的属性。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>借用构造函数继承父类型属性</title>
<script>
/* 核心原理:通过call()将父类型的this指向子类型的this,这样就可以实现子类型继承父类型的属性。 */
function Father(uname, age) {
//this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
function Son(uname, age) {
//this 指向子构造函数的对象实例
Father.call(this, uname, age);
}
var son = new Son('刘德华',50);
console.log(son);
</script>
</head>
<body>
</body>
</html>
2.3 借用原型对象继承父类型方法
一般情况下,对象的方法都在构造函数的原型对象中设置,通过构造函数无法继承父类方法
。核心原理:
- 将子类所共享的方法提出出来,让子类的
prototype 原型对象 = new 父类()
。 - 本质:子类原型对象等于是实例化父类,因为父类实例化之后另外开辟空间,就不会影响原来父类原型对象。
- 将子类的constructor重新指向子类的构造函数。
- 将子类所共享的方法提出出来,让子类的
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>借用原型对象继承父类型方法</title>
<script>
/* 核心原理:通过call()将父类型的this指向子类型的this,这样就可以实现子类型继承父类型的属性。 */
function Father(uname, age) {
//this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
Father.prototype.money = function () {
console.log('赚钱');
}
function Son(uname, age) {
//this 指向子构造函数的对象实例
Father.call(this, uname, age);
}
// Son.prototype = Father.prototype; 这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会随之发生改变。
Son.prototype = new Father();
Son.prototype.constructor = Son;
Son.prototype.exam = function () {
console.log('儿子要考试')
}
var son = new Son('刘德华', 50);
console.log(son);
</script>
</head>
<body>
</body>
</html>
第三章:ES5中的新增方法
3.1 概述
- ES5给我们新增了一些方法,可以很方便的操作数组或者字符串,这些方法主要包括:
- ①数组方法。
- ②字符串方法。
- ③对象方法。
3.2 数组方法
- forEach()方法:
/*
currentValue:数组当前项的值
index:数组当前项的索引
arr:数组对象本身
*/
array.forEach(function(currentValue,index,arr));
- 示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>forEach方法</title>
<script>
var arr = [1, 2, 3, 4, 5];
// currentValue 数组当前项的值
// index 数组当前项的索引
// arr 数组本身
arr.forEach(function (currentValue, index, arr) {
console.log('每个数组元素:' + currentValue);
console.log('每个数组元素的索引号:' + index);
console.log('数组本身:' + arr);
})
</script>
</head>
<body>
</body>
</html>
- filter()方法:创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,
主要用来筛选数组
/*
currentValue:数组当前项的值
index:数组当前项的索引
arr:数组对象本身
*/
array.filter(function(currentValue,index,arr));
- 返回的是一个新的数组。
- 示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>filter方法</title>
<script>
/* 创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用来筛选数组,返回的是一个新的数组。 */
var arr = [1, 2, 3, 4, 5];
var newArr = arr.filter(function (value, index, arr) {
return value >= 2;
});
console.log(newArr);
</script>
</head>
<body>
</body>
</html>
- some()方法:用于检测数组中的元素是否满足指定条件,通俗点的讲查找数组中是否有满足条件的元素。
array.some(function(currentValue, index, arr))
- 注意它返回值是布尔值, 如果查找到这个元素, 就返回true , 如果查找不到就返回false。
- 如果找到第一个满足条件的元素,则终止循环. 不在继续查找。
- 示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>some方法</title>
<script>
/* 用于检测数组中的元素是否满足指定条件,通俗点的讲查找数组中是否有满足条件的元素。 */
var arr = [1, 2, 3, 4, 5];
var bool = arr.some(function (value, index, array) {
return value == 5;
});
console.log(bool); //true
</script>
</head>
<body>
</body>
</html>
- 示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>查询商品案例</title>
<style>
.box {
width: 1000px;
height: 500px;
margin: 100px auto;
}
.box div {
width: 100%;
}
.box table {
width: 500px;
margin: 0 auto;
margin-top: 20px;
border-collapse: collapse;
}
.box table th, td {
text-align: center;
border: 1px solid #000;
}
</style>
</head>
<body>
<!--
1.把数据渲染到页面中。
2.根据价格显示数据。
3.根据商品名称显示数据。
-->
<div class="box">
<div>
按照价格查询:
<input type="text" class="from"> - <input type="text" class="to">
<button class="search">搜索</button>
按照商品价格查询:
<input type="text" class="price">
<button class="query">查询</button>
</div>
<table>
<thead>
<tr>
<th>id</th>
<th>产品名称</th>
<th>价格</th>
</tr>
</thead>
<tbody>
<!--<tr>
<td>1</td>
<td>小米</td>
<td>3999</td>
</tr>-->
</tbody>
</table>
</div>
<script>
var goods = [{
id: 1,
name: '小米',
price: 3999
}, {
id: 2,
name: 'oppo',
price: 999
}, {
id: 3,
name: '荣耀',
price: 1299
}, {
id: 4,
name: '华为',
price: 1999
}];
var tbody = document.querySelector('tbody');
function render(goods, tbody) {
tbody.innerHTML = '';
goods.forEach(function (value) {
var tr = ' <tr>\n' +
' <td>' + value.id + '</td>\n' +
' <td>' + value.name + '</td>\n' +
' <td>' + value.price + '</td>\n' +
' </tr>'
tbody.insertAdjacentHTML('beforeend', tr);
})
}
//初始化的时候渲染数据
render(goods, tbody);
var from = document.querySelector('.from');
var to = document.querySelector('.to');
var search = document.querySelector('.search');
search.onclick = function () {
if (from.value && to.value) {
var newGoods = goods.filter(function (value) {
return value.price >= from.value && value.price <= to.value;
})
render(newGoods, tbody);
} else {
render(goods, tbody);
}
}
var query = document.querySelector('.query');
var price = document.querySelector('.price');
query.onclick = function () {
if (price.value) {
var newGoods = goods.filter(function (value) {
return value.price == price.value;
})
render(newGoods, tbody);
} else {
render(goods, tbody);
}
}
</script>
</body>
</html>
3.3 字符串方法
- trim()方法:会将一个字符串的两端删除空白字符。
str.trim();
trim()方法并不会影响原字符串本身,它返回的是一个新的字符串。
- 示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>trim方法</title>
<script>
var str = ' 我爱你啊 ';
console.log(str.trim());
</script>
</head>
<body>
</body>
</html>
3.4 对象方法
- Object.keys()方法:返回对象自身所有的属性。
Object.keys()
- 效果类似于for…in。
返回一个由属性名组成的数组。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Object.keys()</title>
<script>
var stu = {
name: '许大仙',
age: 18
}
// 返回对象自身所有的属性
// 返回一个由属性名组成的数组
var arr = Object.keys(stu);
arr.forEach(function (value) {
console.log(value);
})
//name
//age
</script>
</head>
<body>
</body>
</html>
- Object.defineProperty()方法:定义新属性或修改原有的属性。
//obj:目标对象,必须。
//prop:定义或修改属性的名字,必须。
//descriptor:必须,目前属性所拥有的特性
Object.defineProperty(obj, prop, descriptor)
descriptor,以对象形式{}书写。
- value: 设置属性的值,默认为undefined。
- writable: 值是否可以重写。true | false, 默认为false。
- enumerable: 目标属性是否可以被枚举。true | false,默认为false。
- configurable: 目标属性是否可以被删除或是否可以再次修改特性,true | false,默认为false。
- 示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Object.defineProperty方法</title>
<script>
/* 原来新增或修改对象的属性 */
var obj = {
id: 1,
name: '小米',
price: 1999
}
obj.num = 1000;
console.log(obj.num);
/* Object.defineProperty():定义新属性或修改原有属性 */
var obj2 = {
id: 1,
name: '小米',
price: 1999
}
/* 如果有属性,则覆盖属性值;如果没有该属性,则新增该属性。 */
Object.defineProperty(obj2, 'num', {
value: 1000
});
Object.defineProperty(obj2, 'price', {
value: 9.9
});
Object.defineProperty(obj2, 'id', {
//false表示不允许重写,不允许修改属性值。
writable: false
});
Object.defineProperty(obj2, 'address', {
value: '中国山东蓝翔技校xxx单元',
/* 目标属性是否可以被枚举 */
enumerable: false
});
console.log(Object.keys(obj2));;
console.log(obj2);
</script>
</head>
<body>
</body>
</html>