大纲
(慕课的图先用着)
scope-作用域
作用域是指程序源代码中定义变量的区域,规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。
JavaScript作用域共有两种主要的工作模型——词法作用域(静态作用域)和动态作用域。
JavaScript默认采用词法作用域(lexical scoping),也就是静态作用域。
词法作用域是由开发者在写代码时将变量和块作用域写在哪里来决定的。由此出现全局作用域,函数作用域(局部作用域),块状作用域
动态作用域一般由this产生
注:1.如果一个变量或者表达式不在“当前的作用域”,那么JavaScript机制会继续沿着作用域链向上查找直到全局作用域(global或浏览器中的window)如果找不到将不判定为不可被使用。
2.作用域也会根据代码层次分层,作用域之间形成父子关系,嵌套关系。子作用域可以访问父作用域。通常沿着链式的作用域链查找向上查找,而不能从父作用域引用访问子作用域的变量和引用
全局作用域
所有在全局作用域中声明的变量、函数都会变成window对象的属性和方法
变量在函数或者代码块{}外定义,即为全局作用域。一般可以在页面任何地方访问
var oneName = " Volvo";
// 此处可调用 oneName 变量
function myFunction() {
// 函数内可调用 oneName 变量
}
if(true){
//代码块内可调用oneName变量
}
在函数或者代码块{}内未定义的变量也拥有全局作用域。该变量将作为global或者window的属性存在
// 此处可调用 oneName 变量
function myFunction() {
carName = "Volvo";
// 函数内可调用 oneName 变量
}
if(true){
//代码块内可调用oneName变量
}
注:在函数内部或者代码块中没有定义的变量实际上是作为global/window的属性存在,而不是全局变量。该变量是可以被delete掉,而全局变量不可以。因为通过var语句添加的全局变量有一个configcurable属性,默认值为false。可以通过Object.getOwnPropertyDescriptor(window,"...")查看
window对象上的属性在书写时可以省略window.
函数作用域(局部作用域)
在函数内部定义的变量对外是封闭的,从外层无法直接访问函数内部的作用域
function bar() {
var testValue = 'inner';
}
console.log(testValue); // 报错:ReferenceError: testValue is not defined
如果想读取函数内部的变量,必须借助return或者闭包
//使用return方式
function bar(value) {
var testValue = 'inner';
return testValue + value;
}
console.log(bar('fun')); // "innerfun"
return 是函数对外交流的出口,而 return 可以返回的是函数,根据作用域的规则,函数bar内部的子函数inner是可以获取函数bar作用域内的变量result
//使用闭包方式
function bar(value) {
var testValue = 'inner';
var result = testValue + value;
function innser() {
return result;
};
return innser();
}
console.log(bar('fun')); // "innerfun"
块状作用域
在其他编程语言中,块状作用域是很熟悉的概念,但是在JavaScript中不被支持。在ES6中已经有了{}块状作用域。
if(true){
let a = 1
console.log(a) //1
}
console.log(a) //a is not defined
注:事实上块状作用域在ES3就已经存在,只是不明显。比如catch块捕获的异常仅在catch块可见
词法作用域(静态作用域)与动态作用域
在javaScript采用词法作用域,执行foo函数,先从foo函数内部查找局部变量value,如果没有,就根据书写的位置,查找上面一层的代码,也就是value等于1,所以结果会打印1
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar();
// 结果是1
这里 bind 已经把作用域的范围进行了修改指向了 { a: 2 }
,而 this 指向的是当前作用域对象
window.a = 3
function test () {
console.log(this.a)
}
test.bind({ a: 2 })() // 2
test() // 3
如果要开启动态作用域请借助 bind、with、eval 等。
let&Const
let&Const是ES6之后的语法。let拥有的特性Const都拥有。在ES6之后声明变量有6个方式var,function,let,const,import,class
var命令和function命令声明的全局变量依然是顶层对像(window/global)的成员。但let,const,class命令声明的全局变量,不在属于顶层对象的成员。从ES6开始全局变量将逐步与顶层对象的属性脱钩。
注:let和Const声明的变量去哪了?参考https://juejin.im/post/5c0be11b6fb9a049df23e388#heading-1
let
let声明的变量拥有块级作用域
{
let a = 1
}
console.log(a); //undefined
let声明的全局变量不是全局对象的属性
不可以通过window变量名的方式访问这些变量,而var声明的全局变量是window的属性,是可以通过window变量名的访问
var a = 1
console.log(window.a); //1
let a = 1
console.log(window.a); // undefined
用let重定义变量会抛出一个语法错误,var可以重复定义。
var a = 1
var a = 2
console.log(a) //2
let a = 1
let a = 2
// VM131:1 Uncaught SyntaxError: Identifier 'a' has already been declared
// at <anonymous>:1:1
let声明的变量不会进行变量提升,存在暂时性死区(TDZ)。在死区结束前变量不会初始化
function test () {
console.log(a)
var a = 1
}
test() //undefined
function test () {//TDZ开始
console.log(a)//
...
...
let a = 1 //TDZ结束
}
test()
// Uncaught ReferenceError: Cannot access 'a' before initialization
const
Const除了具有let的块级作用域和不会变量,并且它定义的常量,在用const定义变量后,就不能修改。对其修改会抛出异常
const PI = 3.1415;
console.log(PI);
PI = 22;
console.log(PI);
// Uncaught TypeError: Assignment to constant variable.
const声明的变量必须进行初始化,否则会抛出异常Uncaught SyntaxError: Missing initializer in const declaration
const PI
PI = 3.1415
// Uncaught SyntaxError: Missing initializer in const declaration
Array
ES6新增了一些实用的原生API,方便开发者对Array的操控性更强。
ES5操作数组的方法 concat、join、push、pop、shift、unshift、slice、splice、substring 和 substr 、sort、 reverse、indexOf 和 lastIndexOf 、every、some、filter、map、forEach、reduce
ES6新增操作数组的方法 find、findIndex、fill、copyWithin、Array.from、Array.of、entries、values、key、includes
围绕数组产生的动作无非是遍历,转换,生成,查找。。。。。。(数据库增删改查一样一样的)
数组遍历的方法有很多例如for循环,forEach,every,for...in,for...of等等
数组的遍历
举几个例子
for循环
for (var i = 0; i < array.length; i++) {
console.log(array[i]);
}
forEach
forEach ( ) 方法。等同于for,不支持break;continue,会抛出异常
forEach()对于空数组是不会执行回调函数的
array.forEach(function(i){
console.log(i);
})
[1,2,3,4,5].forEach(function(i){
if(i===2){
return;
}else{
console.log(i) //1,3,4,5
}
})
every
every()方法遍历可以达到break效果。
every的代码块种仍然不能使用break,continue,会抛出异常
[1,2,3,4,5].every(function(i){
if(i===2){
return false; //return false等同于break
}else{
console.log(i)
return true // return true 等同于continue
}
})
for...in
for... in 的目的是用来遍历对象,而数组刚好是可遍历对象。。。
for (var index in array) {
//index是索引值,是字符串
console.log(array[index]);
}
for...of
for…of是支持 break、continue的,所以在功能上非常贴近原生的 for。
for…of 遍历的是一切可遍历的元素(数组、对象、集合)等。ES6 中允许开发者自定义遍历
for (variable of iterable) {}
variable:每个迭代的属性值被分配给该变量
iterable:一个具有可枚举属性并且可以迭代的对象
数组转换功能
伪数组 具有length属性; 按索引方式存储数据; 不能调用数组的方法
ES5伪数组转换为数组
let args = [].slice.call(arguments)
let imgs= [].slice.call(document.querySelectorAll('img'))//NodeList
ES6伪数组转换为数组
let args = Array.from(arguments)
let imgs = Array.from(document.querySelectorAll('img'))
Array.from(arrayLike,mapFn,thisArg)
//arrayLike 必选 想要转换成数组的伪数组对象或可迭代对象
//mapFn 可选 新数组中的每个元素会执行该回调函数
//thisArg 可选 执行回调函数 mapFn 时 this 对象
数组的生成
ES5初始化一个长度为5的数组,每个数组元素有默认值
Array.from({ length: 5 }, function () { return 1 })
ES6初始化一个长度为5的数组,每个数组元素有默认值
Array.prototype.from()方法
Array.from({ length: 5 }, function () { return 1 })
Array.prototype.of()方法
Array.of(element0[, element1[, ...[, elementN]]])
elementN 任意个参数,将按顺序成为返回数组中的元素。
Array.of(7); // [7]
Array.of(1, 2, 3); // [1, 2, 3]
Array(7); // [ , , , , , , ]
Array(1, 2, 3); // [1, 2, 3]
Array.prototype.fill()方法
fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。
let array = [1, 2, 3, 4]
array.fill(0, 1, 2)
// [1,0,3,4]
arr.fill(value[, start[, end]])
value 必选 用来填充数组元素的值
start 可选 起始索引,默认值为0
end 可选 终止索引,默认值为this.length
数组的查找
ES6新增查找方法find(),findIndex()
Array.prototype.find()方法
find() 方法返回数组中满足提供的测试函数的第一个元素的值,否则返回 undefined。
let array = [5, 12, 8, 130, 44];
let found = array.find(function(element) {
return element > 10;
});
console.log(found);// 12
array.find(function(currentValue, index, arr),thisValue)
//function(currentValue, index, arr) 在数组每一项上执行的函数
//currentValue 当前遍历到的元素
//index 当前遍历到的索引
//array 数组本身
//thisArg 执行回调时用作this的对象
Array.prototype.findIndex()方法
findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。其实这个和 find() 是成对的,不同的是它返回的是索引而不是值