javascript基础
This commit is contained in:
parent
2f6d199dc1
commit
990ebadcad
@ -1,6 +1,31 @@
|
||||
# JavaScript 基础
|
||||
<nav>
|
||||
<a href="#一概念简介">一、概念简介</a><br/>
|
||||
<a href="#二基本类型">二、基本类型</a><br/>
|
||||
<a href="#21-数值类型">2.1 数值类型</a><br/>
|
||||
<a href="#22-字符类型">2.2 字符类型</a><br/>
|
||||
<a href="#23-基本类型检测">2.3 基本类型检测</a><br/>
|
||||
<a href="#三引用类型">三、引用类型</a><br/>
|
||||
<a href="#31-Object-类型">3.1 Object 类型</a><br/>
|
||||
<a href="#32-Array-类型">3.2 Array 类型</a><br/>
|
||||
<a href="#33-Date-类型">3.3 Date 类型</a><br/>
|
||||
<a href="#34-Funcation-类型">3.4 Funcation 类型</a><br/>
|
||||
<a href="#35-引用类型检测">3.5 引用类型检测</a><br/>
|
||||
<a href="#四内置对象">四、内置对象</a><br/>
|
||||
<a href="#41-Global-对象">4.1 Global 对象</a><br/>
|
||||
<a href="#42--window-对象">4.2 window 对象</a><br/>
|
||||
<a href="#五作用域与闭包">五、作用域与闭包</a><br/>
|
||||
<a href="#51--作用域">5.1 作用域</a><br/>
|
||||
<a href="#52-作用域链">5.2 作用域链</a><br/>
|
||||
<a href="#53-闭包">5.3 闭包</a><br/>
|
||||
<a href="#六对象设计">六、对象设计</a><br/>
|
||||
<a href="#61-数据属性"> 6.1 数据属性</a><br/>
|
||||
<a href="#62-访问器属性">6.2 访问器属性</a><br/>
|
||||
<a href="#63--读取属性">6.3 读取属性</a><br/>
|
||||
<a href="#64-创建对象">6.4 创建对象</a><br/>
|
||||
</nav>
|
||||
|
||||
## 一、简介
|
||||
## 一、概念简介
|
||||
|
||||
JavaScript 是一种专为与网页交互而设计的脚本语言,由以下三个部分组成:
|
||||
|
||||
@ -8,31 +33,156 @@ JavaScript 是一种专为与网页交互而设计的脚本语言,由以下三
|
||||
- **文档对象模型 ( DOM )**:提供访问和操作网页内容的方法和接口;
|
||||
- **浏览器对象模型 ( BOM )**:提供与浏览器交互的方法和接口。
|
||||
|
||||
ECMAScript 提供了语言的核心功能,它定义了以下七种数据类型:
|
||||
|
||||
- 六种基本数据类型:Undefined,Null,Boolean,Number,String,Symbol ( ES 6 新增 );
|
||||
- 一种引用类型:统称为 Object 类型;具体又细分为 Object,Array,Date,RegExp,Function 等类型。另外和 Java 语言类似,对于布尔,数值,字符串等基本类型,分别存在其对应的包装类型 Boolean,Number,String,但通常我们并不会使用到这些包装类型,只需要使用其基本类型即可。
|
||||
|
||||
## 二、基本概念
|
||||
## 二、基本类型
|
||||
|
||||
### 2.1 数据类型
|
||||
### 2.1 数值类型
|
||||
|
||||
ECMAScript 提供了以下六种数据类型:
|
||||
**1. 进制数值**
|
||||
|
||||
- 五种基本数据类型:Undefined,Null,Boolean,Number,String ;
|
||||
- 一种复杂数据类型:Object 。
|
||||
ECMAScript 中的 Number 支持以下三种常用进制:
|
||||
|
||||
JavaScript 是一种弱类型的语言,在声明变量时候可以不必指明其具体类型,而是由程序进行推断。如果想要知道变量的具体类型,可以使用 typeof 关键字,它的返回情况如下:
|
||||
+ 十进制:正常数值就是十进制;
|
||||
+ 八进制:八进制字面值的第一位必须是零(0),然后是八进制数字序列(0~7);
|
||||
+ 十六进制:十六进制字面值的前两位必须是 0x,后跟任意的十六进制数字(0~9 及 A~F)。
|
||||
|
||||
- **undefined**:如果对应的值未定义;
|
||||
- **boolean**:如果对应的值是布尔值;
|
||||
- **string**:如果对应的值是字符串;
|
||||
- **number**:如果对应的值是数值;
|
||||
- **object**:如果对应的值是对象或 null;
|
||||
- **function**:如果对应的值是函数则返回 function。 函数在本质上也是对象,但是由于其一等公民的特殊地位,所以将其和其他普通对象进行区分是很有必要的,因此 typeof 对其检测时会返回 function ,而不是 object 。
|
||||
```javascript
|
||||
console.log(56); // 56
|
||||
console.log(070); // 56
|
||||
console.log(0x38); // 56
|
||||
```
|
||||
|
||||
### 2.2 数值类型
|
||||
**2. 浮点数值**
|
||||
|
||||
ECMAScript 的数值类型同样支持浮点数,但是由于保存浮点数值需要的内存空间是保存整数值的两倍,因此 ECMAScript 会尽量将浮点数值转换为整数值存储。
|
||||
|
||||
```javascript
|
||||
var a = 10.0;
|
||||
console.log(a); // 10
|
||||
```
|
||||
|
||||
### 2.3 字符类型
|
||||
和其他语言类似,浮点数中的数值也是不精准的,示例如下:
|
||||
|
||||
```javascript
|
||||
var a = 0.1; var b = 0.2;
|
||||
|
||||
a + b ; // 0.30000000000000004
|
||||
a+b === 0.3 ; // false
|
||||
```
|
||||
|
||||
如果想要对浮点数进行精确计算,可以使用 [decimal.js](https://github.com/MikeMcl/decimal.js) ,[ math.js](https://github.com/josdejong/mathjs) 等第三方库。
|
||||
|
||||
**3. 科学计数法**
|
||||
|
||||
ECMAScript 支持使用科学计数法来表达数值:
|
||||
|
||||
```javascript
|
||||
8e-2 // 0.08
|
||||
8e2 // 800
|
||||
```
|
||||
|
||||
**4. parseInt() \ parseFloat() **
|
||||
|
||||
parseInt 可以用于解析字符串并返回整数,parseFloat 用于解析字符串并返回浮点数:
|
||||
|
||||
```javascript
|
||||
parseInt("56"); // 56
|
||||
parseInt("0x38", 16); // 56 支持使用第二个参数来表示转换的进制
|
||||
parseInt("56.6"); // 56
|
||||
|
||||
parseFloat("12.2"); // 12.2
|
||||
|
||||
parseInt("blue"); // NaN NaN用于表示一个本来要返回数值的操作却未返回数值的情况
|
||||
```
|
||||
|
||||
**5. toFixed() **
|
||||
|
||||
toFixed 用于保留指定位数的小数,但需要注意的是其四舍五入的行为是不确定的:
|
||||
|
||||
```javascript
|
||||
1.35.toFixed(1) // 1.4 正确
|
||||
1.335.toFixed(2) // 1.33 错误
|
||||
1.3335.toFixed(3) // 1.333 错误
|
||||
1.33335.toFixed(4) // 1.3334 正确
|
||||
1.333335.toFixed(5) // 1.33333 错误
|
||||
1.3333335.toFixed(6) // 1.333333 错误
|
||||
```
|
||||
|
||||
想要解决这个问题,可以通过判断最后一位是否大于或等于5来决定是否需要进位,重写 toFixed 方法的具体代码如下:
|
||||
|
||||
```javascript
|
||||
// toFixed兼容方法
|
||||
Number.prototype.toFixed = function(len){
|
||||
if(len>20 || len<0){
|
||||
throw new RangeError('toFixed() digits argument must be between 0 and 20');
|
||||
}
|
||||
// .123转为0.123
|
||||
var number = Number(this);
|
||||
if (isNaN(number) || number >= Math.pow(10, 21)) {
|
||||
return number.toString();
|
||||
}
|
||||
if (typeof (len) == 'undefined' || len == 0) {
|
||||
return (Math.round(number)).toString();
|
||||
}
|
||||
var result = number.toString(),
|
||||
numberArr = result.split('.');
|
||||
|
||||
if(numberArr.length<2){
|
||||
//整数的情况
|
||||
return padNum(result);
|
||||
}
|
||||
var intNum = numberArr[0], //整数部分
|
||||
deciNum = numberArr[1],//小数部分
|
||||
lastNum = deciNum.substr(len, 1);//最后一个数字
|
||||
|
||||
if(deciNum.length == len){
|
||||
//需要截取的长度等于当前长度
|
||||
return result;
|
||||
}
|
||||
if(deciNum.length < len){
|
||||
//需要截取的长度大于当前长度 1.3.toFixed(2)
|
||||
return padNum(result)
|
||||
}
|
||||
//需要截取的长度小于当前长度,需要判断最后一位数字
|
||||
result = intNum + '.' + deciNum.substr(0, len);
|
||||
if(parseInt(lastNum, 10)>=5){
|
||||
//最后一位数字大于5,要进位
|
||||
var times = Math.pow(10, len); //需要放大的倍数
|
||||
var changedInt = Number(result.replace('.',''));//截取后转为整数
|
||||
changedInt++;//整数进位
|
||||
changedInt /= times;//整数转为小数,注:有可能还是整数
|
||||
result = padNum(changedInt+'');
|
||||
}
|
||||
return result;
|
||||
//对数字末尾加0
|
||||
function padNum(num){
|
||||
var dotPos = num.indexOf('.');
|
||||
if(dotPos === -1){
|
||||
//整数的情况
|
||||
num += '.';
|
||||
for(var i = 0;i<len;i++){
|
||||
num += '0';
|
||||
}
|
||||
return num;
|
||||
} else {
|
||||
//小数的情况
|
||||
var need = len - (num.length - dotPos - 1);
|
||||
for(var j = 0;j<need;j++){
|
||||
num += '0';
|
||||
}
|
||||
return num;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 参考自:[*js中小数四舍五入和浮点数的研究*](http://caibaojian.com/js-tofixed.html)
|
||||
|
||||
### 2.2 字符类型
|
||||
|
||||
**1. 字符串表示**
|
||||
|
||||
@ -40,7 +190,7 @@ ECMAScript 支持使用双引号 ( " ) 或单引号 ( ' ) 来表示字符串,
|
||||
|
||||
```javascript
|
||||
var lang = "Java";
|
||||
/*创建一个能容纳 10 个字符的新字符串用于填充"Java"和"Script",之后再销毁原有的字符串"Java"和"Script"*/
|
||||
/*程序会创建一个能容纳 10 个字符的新字符串用于填充"Java"和"Script",之后再销毁原有的字符串"Java"和"Script"*/
|
||||
lang = lang + "Script";
|
||||
```
|
||||
|
||||
@ -96,107 +246,21 @@ alert(stringValue.substring(3, -4)); // "hel" 按照规则等价于:
|
||||
alert(stringValue.substr(3, -4)); // ""(空字符串) 按照规则等价于: substr(3,0)
|
||||
```
|
||||
|
||||
### 2.3 基本类型检测
|
||||
|
||||
JavaScript 是一种弱类型的语言,在声明变量时候可以不必指明其具体类型,而是由程序进行推断。如果想要知道变量具体属于哪一个基础类型,可以使用 typeof 关键字,它的返回情况如下:
|
||||
|
||||
- **undefined**:如果对应的值未定义;
|
||||
- **boolean**:如果对应的值是布尔值;
|
||||
- **string**:如果对应的值是字符串;
|
||||
- **number**:如果对应的值是数值;
|
||||
- **object**:如果对应的值是对象或 null;
|
||||
- **function**:如果对应的值是函数则返回 function。 函数在本质上也是对象,但是由于其一等公民的特殊地位,所以将其和其他普通对象进行区分是很有必要的,因此 typeof 对其检测时会返回 function ,而不是 object 。
|
||||
|
||||
|
||||
### 2.4 理解函数
|
||||
## 三、引用类型
|
||||
|
||||
ECMAScript 使用 function 关键字来声明函数,但和其他语言不同的是,ECMAScript 中对于函数参数的限制是非常宽松的,例如你在定义函数时定义了两个参数,但在调用时可以只传递一个参数、也可以传三个参数,甚至不传递,示例如下:
|
||||
|
||||
```java
|
||||
function test(first, second) {
|
||||
console.log("first:" + first + ",second:" + second);
|
||||
}
|
||||
test(1) // first:1,second:undefined
|
||||
test(1,2) // first:1,second:2
|
||||
test(1,2,3) // first:1,second:2
|
||||
```
|
||||
|
||||
之所以能实现这样的效果,是因为 ECMAScript 在函数内部使用了一个数组 arguments 来维护所有参数,函数接收到的始终都是这个数组,而在实际使用时指向的也是这个数组中的具体元素,所以以上的函数等价于下面的函数:
|
||||
|
||||
```javascript
|
||||
function test(first, second) {
|
||||
console.log("first:" + arguments[0] + ",second:" + arguments[1]);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 三、作用域与闭包
|
||||
|
||||
### 3.1 作用域
|
||||
|
||||
在 ECMAScript 6 之前,只存在两种作用域,即:全局作用域 和 函数作用域,不存在块级作用域。这意味着在除了函数外的任何代码块中使用 var 关键字声明的变量都会被提升为全局变量,示例如下:
|
||||
|
||||
```javascript
|
||||
function test() {
|
||||
var age =12;
|
||||
}
|
||||
age // age is not defined
|
||||
|
||||
if (true) {
|
||||
var name = "heibaiying";
|
||||
}
|
||||
name // heibaiying
|
||||
```
|
||||
|
||||
这种情况同样适用与 for 循环代码块:
|
||||
|
||||
```javascript
|
||||
for (var i = 0; i < 10; i++) {}
|
||||
console.log(i); // 10
|
||||
```
|
||||
|
||||
### 3.2 作用域链
|
||||
|
||||
由于函数作用域的存在,函数内的变量不能被外部访问,但是函数内的变量可以被其内部的函数访问,并且函数也可以访问其父级作用域上的变量,从而形成一条从其自身作用域到全局作用域的链条,示例如下:
|
||||
|
||||
```javascript
|
||||
var global = "global";
|
||||
var outer = "outer global";
|
||||
|
||||
(function outer() {
|
||||
var outer = "outer";
|
||||
|
||||
function inner() {
|
||||
console.log(global, outer);
|
||||
}
|
||||
|
||||
inner()
|
||||
})();
|
||||
|
||||
// 输出:global outer
|
||||
```
|
||||
|
||||
### 3.3 闭包
|
||||
|
||||
由于函数作用域的存在,函数内的变量不能被外部访问,这可以保证变量的私有性。但如果你想允许外部对内部变量进行特定操作,可以通过闭包来实现。闭包是指有权访问另一个函数作用域中的变量的函数。示例如下:
|
||||
|
||||
```java
|
||||
var contain = function () {
|
||||
|
||||
var arr = [];
|
||||
|
||||
return {
|
||||
push: function () {
|
||||
arr.push(...arguments);
|
||||
},
|
||||
|
||||
get: function () {
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var ctn = contain();
|
||||
ctn.push(1, 2, 3, 4);
|
||||
ctn.get(); // [ 1, 2, 3, 4 ]
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 四、引用对象
|
||||
|
||||
### 4.1 Object 类型
|
||||
### 3.1 Object 类型
|
||||
|
||||
创建 Object 实例有以下两种方式:
|
||||
|
||||
@ -216,7 +280,7 @@ var user = {
|
||||
};
|
||||
```
|
||||
|
||||
### 4.2 Array 类型
|
||||
### 3.2 Array 类型
|
||||
|
||||
创建数组也有两种方式,基于构造函数的方式和基于对象字面量的方式:
|
||||
|
||||
@ -403,7 +467,7 @@ var sum02 = values.reduceRight(function (prev, cur, index, array) {
|
||||
}); // 15
|
||||
```
|
||||
|
||||
### 4.3 Date 类型
|
||||
### 3.3 Date 类型
|
||||
|
||||
创建一个日期对象,可以使用 new 操作符和 Date 构造函数:
|
||||
|
||||
@ -436,7 +500,7 @@ console.log(date.toString()); // Wed Aug 08 2018 08:30:20 GMT+0800 (GMT+08:00)
|
||||
console.log(date.valueOf()); // 1533688220000
|
||||
```
|
||||
|
||||
用于 valueOf() 返回的是日期的时间戳格式,所以对于 date 对象,可以直接使用比较运算符来比较其大小:
|
||||
由于 valueOf() 返回的是日期的时间戳格式,所以对于 date 对象,可以直接使用比较运算符来比较其大小:
|
||||
|
||||
```javascript
|
||||
var date01 = new Date(2018, 7, 8, 8, 30, 20);
|
||||
@ -458,17 +522,322 @@ console.log(date01 < date02); // flase
|
||||
- **getSeconds() \ setSeconds(秒)** :返回和设置日期中的秒数(0到59);
|
||||
- **getMilliseconds() \ setMilliseconds(毫秒)** :返回和设置日期中的毫秒数。
|
||||
|
||||
### 4.5 Funcation 类型
|
||||
### 3.4 Funcation 类型
|
||||
|
||||
**1. 函数参数**
|
||||
|
||||
ECMAScript 使用 function 关键字来声明函数,但和其他语言不同的是,ECMAScript 中函数对于参数的限制是非常宽松的,例如你在定义函数时定义了两个参数,但在调用时可以只传递一个参数、也可以传三个参数,甚至不传递,示例如下:
|
||||
|
||||
```java
|
||||
function test(first, second) {
|
||||
console.log("first:" + first + ",second:" + second);
|
||||
}
|
||||
test(1) // first:1,second:undefined
|
||||
test(1,2) // first:1,second:2
|
||||
test(1,2,3) // first:1,second:2
|
||||
```
|
||||
|
||||
之所以能实现这样的效果,是因为 ECMAScript 在函数内部使用了一个数组 arguments 来维护所有参数,函数接收到的始终都是这个数组,而在实际使用时指向的也是这个数组中的具体元素,所以以上的函数等价于下面的函数:
|
||||
|
||||
```javascript
|
||||
function test(first, second) {
|
||||
console.log("first:" + arguments[0] + ",second:" + arguments[1]);
|
||||
}
|
||||
```
|
||||
|
||||
**2. 改变函数作用域**
|
||||
|
||||
在 ECMAScript 5 中,每个函数都包含两个非继承而来的方法:apply() 和 call() ,它们都用于在特定的作用域中调用函数。简单来说,可以用这两个方法来改变函数的实际调用对象,从而改变 this 的值,因为 this 总是指向当前函数的实际调用对象:
|
||||
|
||||
```javascript
|
||||
window.color = "red";
|
||||
var o = { color: "blue" };
|
||||
function sayColor(){
|
||||
console.log(this.color);
|
||||
}
|
||||
|
||||
sayColor(); // red
|
||||
sayColor.call(this); // red
|
||||
sayColor.call(window); // red
|
||||
sayColor.call(o); // blue 此时this指向的是函数调用对象,即 o
|
||||
```
|
||||
|
||||
apply() 和 call() 的第一个参数都是指代函数的调用对象,它们的区别主要在于第二个参数:apply() 支持使用数组或 arguments 给调用函数传值,而 call() 给调用函数传值时,必须逐个列举:
|
||||
|
||||
```javascript
|
||||
function sum(num1, num2) {
|
||||
return num1 + num2;
|
||||
}
|
||||
|
||||
function callSum1(num1, num2) {
|
||||
return sum.apply(this, arguments);
|
||||
}
|
||||
|
||||
function callSum2(num1, num2) {
|
||||
return sum.apply(this, [num1, num2]);
|
||||
}
|
||||
|
||||
function callSum3(num1, num2) {
|
||||
return sum.call(this, num1, num2);
|
||||
}
|
||||
|
||||
callSum1(10, 10);
|
||||
callSum2(10, 10);
|
||||
callSum3(10, 10);
|
||||
```
|
||||
|
||||
**3. 绑定函数作用域**
|
||||
|
||||
如果想要将函数绑定在某个特定的作用域上,可以使用 bind() 函数:
|
||||
|
||||
```javascript
|
||||
window.color = "red";
|
||||
var o = { color: "blue" };
|
||||
function sayColor(){
|
||||
console.log(this.color);
|
||||
}
|
||||
|
||||
var objectSayColor = sayColor.bind(o);
|
||||
objectSayColor(); // blue 此时即便是在全局作用域中调用这个函数,其作用域仍然被永远绑定在 o 对象上
|
||||
```
|
||||
|
||||
### 3.5 引用类型检测
|
||||
|
||||
想要检测某个对象是否属于某个引用类型,可以使用 instanceof 关键字:
|
||||
|
||||
```shell
|
||||
var date = new Date();
|
||||
date instanceof Date // true
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 4.8 内置对象
|
||||
## 四、内置对象
|
||||
|
||||
### 4.1 Global 对象
|
||||
|
||||
ECMAScript 中内置了一个全局对象 Global ,任何不属于任何其他对象的属性和方法,最终都是它的属性和方法。 ES 通过该内置对象,提供了一些可以直接调用的全局方法,常用的如下:
|
||||
|
||||
+ **isNaN()**:用于确定一个值是否为 NaN;
|
||||
+ **isFinite()**:用于判断被传入的参数值是否为一个有限数值;
|
||||
+ **parseInt() \ parseFloat()**:解析并返回一个整数 \ 浮点数;
|
||||
+ **encodeURI()**:对 URI 进行编码,但不会对本身属于 URI 的特殊字符进行编码,例如冒号、正斜杠、问号和井号;
|
||||
+ **encodeURIComponent()**:对 URI 进行编码,会对任何非标准字符进行编码。
|
||||
|
||||
### 4.2 window 对象
|
||||
|
||||
ECMAScript 并没有提供任何直接访问 Global 对象的方法,但是浏览器却基于 Global 扩展实现了 window 对象,可以直接在浏览器环境下使用:
|
||||
|
||||
```javascript
|
||||
window.isFinite(12) // true
|
||||
a = 12;
|
||||
window.a // 12
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 五、面向对象
|
||||
## 五、作用域与闭包
|
||||
|
||||
### 5.1 作用域
|
||||
|
||||
在 ECMAScript 6 之前,只存在两种作用域,即:全局作用域 和 函数作用域,不存在块级作用域。这意味着在除了函数外的任何代码块中使用 var 关键字声明的变量都会被提升为全局变量,示例如下:
|
||||
|
||||
```javascript
|
||||
function test() {
|
||||
var age =12;
|
||||
}
|
||||
age // age is not defined
|
||||
|
||||
if (true) {
|
||||
var name = "heibaiying";
|
||||
}
|
||||
name // heibaiying
|
||||
```
|
||||
|
||||
这种情况同样适用与 for 循环代码块:
|
||||
|
||||
```javascript
|
||||
for (var i = 0; i < 10; i++) {}
|
||||
console.log(i); // 10
|
||||
```
|
||||
|
||||
### 5.2 作用域链
|
||||
|
||||
由于函数作用域的存在,函数内的变量不能被外部访问,但是函数内的变量可以被其内部的函数访问,并且函数也可以访问其父级作用域上的变量,从而形成一条从其自身作用域到全局作用域的链条,示例如下:
|
||||
|
||||
```javascript
|
||||
var global = "global";
|
||||
var outer = "outer global";
|
||||
|
||||
(function outer() {
|
||||
var outer = "outer";
|
||||
|
||||
function inner() {
|
||||
console.log(global, outer);
|
||||
}
|
||||
|
||||
inner()
|
||||
})();
|
||||
|
||||
// 输出:global outer
|
||||
```
|
||||
|
||||
### 5.3 闭包
|
||||
|
||||
由于函数作用域的存在,函数内的变量不能被外部访问,这可以保证变量的私有性。但如果你想允许外部对内部变量进行特定操作,可以通过闭包来实现。闭包是指有权访问另一个函数作用域中的变量的函数。示例如下:
|
||||
|
||||
```java
|
||||
var contain = function () {
|
||||
|
||||
var arr = [];
|
||||
|
||||
return {
|
||||
push: function () {
|
||||
arr.push(...arguments);
|
||||
},
|
||||
|
||||
get: function () {
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var ctn = contain();
|
||||
ctn.push(1, 2, 3, 4);
|
||||
ctn.get(); // [ 1, 2, 3, 4 ]
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 六、对象设计
|
||||
|
||||
ECMAScript 中的对象都有两种基本属性:数据属性和访问器属性。
|
||||
|
||||
### 6.1 数据属性
|
||||
|
||||
|
||||
数据属性有以下 4 个描述其行为的特性:
|
||||
|
||||
+ **Enumerable**:表示该属性能否通过 for-in 循环返回;对于直接在对象上定义的属性, 该值默认为 true。
|
||||
+ **Writable**:表示能否修改属性的值;对于直接在对象上定义的属性, 该值默认为 true。
|
||||
+ **Value**:对应属性的数据值。默认值为 undefined。
|
||||
+ **Configurable**:表示能否对属性进行删除,修改等配置操作,对于直接在对象上定义的属性, 该值默认为 true。需要注意的是一旦将该属性的值设置为 false,就不能再将其设置为 true 。即一旦设置为不可配置,就不能再修改为可配置。因为你已经修改为不可配置,此时任何配置操作都无效了,自然修改 Configurable 属性的操作也无效。
|
||||
|
||||
```javascript
|
||||
var person = {age: 12};
|
||||
Object.defineProperty(person, "name", {
|
||||
Enumerable: false,
|
||||
writable: false,
|
||||
value: "heibai"
|
||||
});
|
||||
|
||||
console.log(person.name); // heibai
|
||||
person.name = "ying";
|
||||
console.log(person.name); // ying
|
||||
|
||||
for (var key in person) {
|
||||
console.log("key:" + key + ",value:" + person[key]) /// key:age,value:12
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 访问器属性
|
||||
|
||||
访问器属性也有以下 4 个描述其行为的特性:
|
||||
|
||||
+ **Configurable**:表示能否对属性进行删除,修改等配置操作;对于直接在对象上定义的属性, 该值默认为 true。
|
||||
|
||||
+ **Enumerable**:表示该属性能否通过 for-in 循环返回;对于直接在对象上定义的属性, 该值默认为 true。
|
||||
|
||||
+ **Get**:在读取属性时调用的函数。默认值为 undefined。
|
||||
|
||||
+ **Set**:在写入属性时调用的函数。默认值为 undefined。
|
||||
|
||||
```javascript
|
||||
var student = {
|
||||
_age: null,
|
||||
birthday: new Date(2012,7,2)
|
||||
};
|
||||
Object.defineProperty(student, "age", {
|
||||
|
||||
get: function () {
|
||||
if (this._age == null) {
|
||||
// 如果年龄不存在就根据出生日期计算
|
||||
return new Date().getFullYear() - this.birthday.getFullYear()
|
||||
}else {
|
||||
return this._age;
|
||||
}
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue < 0) {
|
||||
console.log("年龄不能设置为负数");
|
||||
} else {
|
||||
this._age = newValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
student.age = -1; // 输出:年龄不能设置为负数
|
||||
console.log(student.age); // 输出:7
|
||||
student.age = 12;
|
||||
console.log(student.age); // 输出 12
|
||||
```
|
||||
|
||||
|
||||
### 6.3 读取属性
|
||||
|
||||
想要获取一个对象的数据属性和访问器属性,可以使用 Object.getOwnPropertyDescriptor() 方法,类似于其他语言中的反射机制。这个方法接收两个参数:属性所在的对象和要读取属性名称。沿用上面的例子,示例如下:
|
||||
|
||||
```javascript
|
||||
var descriptor = Object.getOwnPropertyDescriptor(student, "age");
|
||||
console.log(descriptor.get); // 输出 [Function: get]
|
||||
console.log(descriptor.enumerable); // 输出 false
|
||||
```
|
||||
|
||||
### 6.4 创建对象
|
||||
|
||||
在 ECMAScript 中,对象就是一种特殊的函数,想要声明一个对象,可以结合使用构造器模式和原型模式:基本属性可以通过构造器传入;但方法声明需要定义在原型属性上,如果直接定义在构造器上,每个对象实例都会创建该方法函数,即每个对象实例调用的都是自己重复声明的方法函数,示例如下:
|
||||
|
||||
```javascript
|
||||
function Person(name, age, job) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
this.job = job;
|
||||
this.friends = ["hei", "bai"];
|
||||
// 方法应该声明在原型属性上,而不是这里
|
||||
this.sayAge = function () {
|
||||
console.log(this.age)
|
||||
}
|
||||
}
|
||||
|
||||
Person.prototype = {
|
||||
constructor: Person,
|
||||
// 方法应该声明在这
|
||||
sayName: function () {
|
||||
alert(this.name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var person1 = new Person("user01", 29, "Software Engineer");
|
||||
var person2 = new Person("user02", 27, "Doctor");
|
||||
|
||||
person1.friends.push("ying");
|
||||
console.log(person1.friends); // [ 'hei', 'bai', 'ying' ]
|
||||
console.log(person2.friends); // [ 'hei', 'bai']
|
||||
console.log(person1 instanceof Person); // true
|
||||
console.log(person1.constructor === Person); // true
|
||||
console.log(person1.sayName === person2.sayName); // true
|
||||
console.log(person1.sayAge===person2.sayAge); // false
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 参考资料
|
||||
|
||||
1. 尼古拉斯·泽卡斯 . JavaScript高级程序设计(第3版). 人民邮电出版社 . 2012-3-29
|
||||
2. [js中小数四舍五入和浮点数的研究](http://caibaojian.com/js-tofixed.html)
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user