JavaScript权威指南笔记

在函数体内,局部变量优先级高于全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var scope = 'global';
function checkscope() {
var scope = 'local';
return scope;
}
checkscope(); //输出‘local’


//下面是局部作用域嵌套的例子
var scope = 'global';
function checkScope() {
var scope = 'local';
function nested() {
var scope = 'nested scope';
return scope;
}
return nested();
}
checkScope(); //输出 "nested scope"

声明提前

JavaScript的函数作用域是指函数内声明的所有变量在函数体内始终是可见的。有意思的是,这意味着变量在声明之前甚至已经可用。JavaScript的这个特性被非正式的称为声明提前(hoisting),即JavaScript函数里声明的所有变量都被提前至函数体的顶部。

1
2
3
4
5
6
var scope = 'global';
function f() {
console.log(scope); //输出undefined
var scope = 'local'; //变量在这里赋值,但在函数体内均有定义
console.log(scope); //输出local
}

上面例子里的f函数,第一行的console.log(scope)没有输出’global’是因为函数作用域的特性,局部变量在整个函数体始终是有定义的,也就是说,在函数体内的局部变量覆盖了同名的全局变量。
没有输出’local’是因为只有在函数执行到var语句的时候,局部变量才会被真正赋值。
上面的例子等价于:

1
2
3
4
5
6
function f() {
var scope; //在函数顶部声明了局部变量
console.log(scope); //输出undefined,是因为变量存在,但没有赋值
scope = 'local';//这里将其初始化并赋值
console.log(scope); //输出local
}

运算符

当运算符在操作数之前,称为‘前增量’运算符,它对操作数进行增量运算,并返回计算后的值;
当运算符在操作数之后,称为‘后增量’运算符,它对操作数进行增量计算,但返回未做增量计算的值。

1
2
var i = 1;j = ++i; //i和j值都是2 
var i = 1;j = i++; // i是2,j是1

throw语句

throw语句用来抛出一个用户自定义的异常。当前函数的执行将被停止(throw之后的语句将不会执行),并且控制将被传递到调用堆栈中的第一个catch块。如果调用者函数中没有catch块,程序将会终止。

1
2
3
4
5
6
7
8
9
function add(num) {
if (num < 0) {
throw new Error('num不能是负数');
}
for (let i = 0; i < num; i++) {
console.log(i);
}
}
add(-1); // 报错

当抛出异常时,JavaScript解释器会立即停止当前正在执行的逻辑,并跳转至就近的异常处理程序。

try/catch/finally语句

1
2
3
4
5
6
7
8
9
try {
// 需要被执行的语句。
}
catch (e) {
// 如果在try块里有异常被抛出时执行的语句。
}
finally {
// 无论是否有异常抛出或捕获这些语句都将执行。
}
1
2
3
4
5
6
7
8
9
10
11
12
13
try{
let num = new Number(prompt('请输入一个正整数'));
if (num < 0) {
throw new Error('num不是正整数');
}
console.log(num * num);
}
catch(e){
console.log(e);
}
finally{
console.log('执行结束');
}

with语句

JavaScript查找某个未使用命名空间的变量时,会通过作用域链来查找,作用域链是跟执行代码的context或者包含这个变量的函数有关。’with’语句將某个对象添加的作用域链的顶部,如果在statement中有某个未使用命名空间的变量,跟作用域链中的某个属性同名,则这个变量将指向这个属性值。如果沒有同名的属性,则将拋出ReferenceError异常。

1
2
3
4
5
6
7
// 如果要获取一个表单里面的address字段的值,通常是这样 document.forms[0].address.value
// 如果这种表达式在代码中多次出现,则可以使用with语句将form对象添加至作用域链的顶层:
with(document.forms[0]){
name.value = '';
age.value = '';
sex.value = '';
}

创建对象

可以通过对象直接量、关键字new和es6中的Object.create()函数来创建对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//对象直接量
let obj = {};
let arr = [];


//new 方法
let obj = new Object();
let arr = new Array();
let date = new Date();
let reg = new RegExp('js'); //定义了名为 reg 的 RegExp 对象,其模式是 "js",当您使用该 RegExp 对象在一个字符串中检索时,将寻找的是字符 "js"。


// Object.create()
const person = {
isHuman: false,
printIntroduction: function () {
console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
}
};

const me = Object.create(person);

me.name = "Matthew"; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten

me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"

属性getter和setter

在es6中,属性值可以用一个或者两个方法替代,这两个方法就是getter和setter。由getter和setter定义的属性称做’存取器属性’(accessor property),它不同于’数据属性’(data property),数据属性只有一个简单的值。

下面是一个get的demo:

1
2
3
4
5
6
7
8
let obj = {
x: 4, // x,y就是数据属性
y: 2,
get z() {
return this.x * this.y;
}
}
console.log(obj.z); // 8

当程序查询存取器的属性值时,JavaScript代用getter方法(无参数),这个方法的返回值就是该属性存取表达式的值。当程序设置一个存取器属性值时,JavaScript调用setter方法,将赋值表达式右侧的值当作参数传入setter。从某种意义上来说,这个方法负责设置属性值,可以忽略该方法的返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<button id="btn">click it</button>
<script type="text/javascript">
let obj = {
//这个数据属性包含下一个序列号
//$符号暗示这个属性是一个私有属性
$n:0,
//返回当前值,然后自增
get next(){return this.$n++},
//给n设置新的值,但只有当它比当前值大时才设置成功
set next(n){
if(n>=this.$n){
this.$n = n;
}
else{
throw "序列号的值不能比当前值小";
}
}
};
document.getElementById('btn').onclick = function increase() {
console.log(obj.next);
obj.next = 10; // 报错,Uncaught 序列号的值不能比当前值小
}
</script>

稀疏数组

稀疏数组就是包含从0开始的不连续索引的数组。通常,数组的length属性值代表数组中元素的个数。如果数组是稀疏的,length属性值大于元素的个数。

清空数组的小技巧

设置length属性为一个小于当前长度的非负整数n时,当前数组中那些索引值大于或等于n的元素将从中删除

1
2
3
4
let arr = [1,2,3,4,5];
console.log(arr); // [1, 2, 3, 4, 5]
arr.length = 0;
console.log(arr); // []

还可以将数组的length属性值设置为大于其当前的长度。实际上并不会向数组中添加新的元素,它只是在数组尾部创建了一个空的区域。

1
2
3
4
let arr = [1,2,3,4,5];
console.log(arr); // [1, 2, 3, 4, 5]
arr.length = 7;
console.log(arr); // [1, 2, 3, 4, 5, empty × 2]

JavaScript权威指南笔记
https://xypecho.github.io/2018/03/27/JavaScript权威指南笔记/
作者
很青的青蛙
发布于
2018年3月27日
许可协议