目录
ES5中的严格模式
JS的语法是什么灵活的,但这种灵活可能会导致全局污染,所以ES5引入了严格模式,听名字就知道这种模式下语法的要求更高,更苛刻。这样可以让JS在更严格的语法下运行,确保安全性,不合理性。
如何使用严格模式?
如果在整个文件使用,将use strict放在文件第一行。
如果在单个函数使用,将use strict放在函数第一行。
第一种方法不利于文件的合并,所以一般使用第二种放在函数第一行。
严格模式到底什么严格?
- 全局变量显示声明
在Java中如果想声明一个全局变量需要在方法外定义,但ES5的正常模式中无需声明就赋值的变量就是全局变量,严格模式中禁止了这样的用法。
javascript
//不使用严格模式就是正确的
v = 1;
- 静态绑定
Javascript语言的一个特点,就是允许"动态绑定",即某些属性和方法到底属于哪一个对象,不是在编译时确定的,而是在运行时(runtime)确定的。
严格模式对动态绑定做了一些限制。某些情况下,只允许静态绑定。也就是说,属性和方法到底归属哪个对象,在编译阶段就确定。这样做有利于编译效率的提高,也使得代码更容易阅读,更少出现意外。
javascript
//(1)禁止使用with语句,使用严格模式后下面的例子就不能成功运行。
var obj = {
x: 10,
y: 20
};
with (obj) {
console.log(x); // 这里会输出10,因为在obj对象的属性中找到了x
}
//(2)创设eval作用域,JavaScript中有全局作用域和函数作用域,严格模式下有了eval作用域。
var x = 2;
console.info(eval("var x = 5; x")); // 5
console.info(x); // 2
- 增强的安全措施
禁止this关键字指向全局对象
javascript
function f(){
return !this;
}
// 返回false,因为"this"指向全局对象,"!this"就是false
function f(){
"use strict";
return !this;
}
// 返回true,因为严格模式下,this的值为undefined,所以"!this"为true。
禁止在函数内部遍历调用栈
在正常模式下可以通过caller获取当前函数的引用,arguments可以获取传入当前函数的参数列表
javascript
function f1(){
"use strict";
f1.caller; // 报错
f1.arguments; // 报错
}
f1();
- 禁止删除变量
严格模式禁止删除变量除非configurable设置为true的对象属性。
javascript
"use strict";
var x;
delete x; // 语法错误
var o = Object.create(null, {'x': {
value: 1,
configurable: true
}});
delete o.x; // 删除成功
- 显式报错
对一个对象的只读属性进行赋值,将报错。
对一个使用getter方法读取的属性进行赋值,会报错。
对禁止扩展的对象添加新属性,会报错。
删除一个不可删除的属性,会报错。
- 函数必须声明在顶层
将来Javascript的新版本会引入"块级作用域"。为了与新版本接轨,严格模式只允许在全局作用域或函数作用域的顶层声明函数。也就是说,不允许在非函数的代码块内声明函数。
ES6中的let、const和块级作用域
在ES6中使用let代替ES5中的var去定义变量,const用来定义常量。我将分别举个例子来讲述他们三个的区别。
var
javascript
var a = 1;
{
var a = 2;
}
console.log(a); //输出2
这段代码输出结果为2,这让一个之前学java的人很震惊。
这段代码说明var定义变量不具备块级作用域特性。var定义的变量是全局性的,容易造成全局污染。
let
let的语法跟java的语法一样,在一个{}(作用域)中定义的变量只能在该作用域使用,离开该作用域访问将报错。
const
一些变量是确定的,声明之后不想被改变就用const定义,当然const定义的常量和let定义的变量都只能在块级作用域中起作用,出了块之后无法访问。
ES6中变量的解构赋值
解构赋值:解析结构,赋予值。(从数组或者对象中提取值然后按一一对应的方式赋给变量)
数组中的解析赋值
在ES6之前当我们想为一组变量赋值:
javascript
var a = 1;
var b = 2;
var c = 3;
//or
var arr = [1, 2, 3];
var a = arr[0];
var b = arr[1];
var c = arr[2];
在ES6中我们可以直接这样进行一组变量的赋值。
javascript
let [a, b, c] = [1, 2, 3];
- 当左边变量大于右边值,剩余变量会被赋值为undefined
- 解析赋值时变量允许有默认值。 let [a, b = 'iyaovo'] = ['wzy']; 这样b没有匹配到东西也有默认值
对象的解构赋值
在ES6之前,当我们调用api接口拿到数据后我们要这样赋值:
javascript
var name = json.name;
var age = json.age;
var sex = json.sex;
现在我们可以解构赋值,直接将一个对象赋值给一组属性,会按照键取值(不像数组是按顺序的)。
javascript
const person = { name: 'iyaovo', age: 21, sex: '男' };
let { name, age, sex } = person;
那么,左边的变量名必须和右边对象的属性名一致吗?
javascript
const person = { name: 'iyaovo', age: 21 };
let { name: myName, age: myAge } = person;
//:左边的对应对象中的属性名,:右边的是定义变量的名字
console.log(myName);
console.log(myAge);
ES6中箭头函数
箭头函数语法
javascript
(参数1, 参数2 ...) => { }
- 如果有且只有一个形参,那么括号可以省略。 参数 => {}
- 如果函数体中只有一条语句,那么{}可以省略。但这条语句必须是return语句
箭头函数是没有箭头名的,你可以直接调用它,或者将它赋给一个变量,通过变量调用它。
这个语法类似Java中的钻石表达式。
例子
javascript
//标准写法
const fn = (a, b) => {
console.log('这是标准');
return a + b;
};
console.log(fn(1, 2));
//精简写法
const fn = a => a + 1;
console.log(fn(1));
箭头函数的this的指向
ES6 之前的普通函数中:this 指向的是函数被调用的对象(也就是说,谁调用了函数,this 就指向谁)。
而 ES6 的箭头函数中:箭头函数本身不绑定 this ,this 指向的是箭头函数定义位置的 this(也就是说,箭头函数在哪个位置定义的,this 就跟这个位置的 this 指向相同)。
javascript
function fn1() {
console.log(this); // 第一个 this
return () => {
console.log(this); // 第二个 this
};
}
箭头函数是在fn1里定义的,所以箭头函数中的this和fn1this的指向一致。
参数默认值
javascript
function fn(param = 'hello') {
console.log(param);
}
直接在形参的位置指定了参数默认值。
字符串、数组、对象的扩展
字符串的扩展
- includes(str)
用于判断当前字符串中是否包含指定的字符串 str
,返回值为布尔类型,区分大小写。
javascript
let str = "Hello, world!";
console.log(str.includes("world")); // true,因为原字符串包含"world"这个子串
console.log(str.includes("World")); // false,区分大小写,原字符串中没有"World"
- startsWith(str)
判断当前字符串是否以指定字符串 str
开头,同样返回布尔值,区分大小写。
javascript
let str = "JavaScript is great";
console.log(str.startsWith("Java")); // true,原字符串以"Java"开头
console.log(str.startsWith("java")); // false,区分大小写,原字符串不以"java"开头
- endsWith(str)
判断当前字符串是否以指定字符串 str
结尾,返回布尔值(true
结尾返回 true
,反之返回 false
),同样区分大小写。
javascript
let str = "This is a sample text.";
console.log(str.endsWith("text.")); // true,原字符串以"text."结尾
console.log(str.endsWith("Text.")); // false,区分大小写,原字符串不以"Text."结尾
- repeat(count)
将当前字符串重复指定的次数 count
并返回新的字符串。参数 count
必须是一个大于等于 0 的整数,如果传入的是小数或者负数等不符合要求的值,会抛出错误。
javascript
let str = "abc";
console.log(str.repeat(3)); // "abcabcabc",原字符串重复3次
console.log(str.repeat(0)); // "",重复0次返回空字符串
// 以下情况会报错,比如传入小数
// console.log(str.repeat(2.5)); // 会抛出错误,参数要求是整数
数组的扩展
- Array.from ()
用于将两类对象转换为真正的数组:类似数组的对象(有length
属性和索引属性)和可迭代对象(如Set
、Map
等)。它返回一个新的数组实例。
- 第一个参数是必需的,它是一个类似数组的对象或可迭代对象,要被转换为数组的对象。
- 第二个参数是一个可选的映射函数,类似于数组的
map
方法,用于对转换后的每个元素进行处理,该函数会接收三个参数:当前元素、当前元素的索引和正在构建的新数组。 - 第三个参数也是可选的,它用于指定
map
函数中的this
指向。
javascript
//单一参数(类似数组的对象)
let arrayLike = {
0: 'a',
1: 'b',
length: 2
};
let newArray = Array.from(arrayLike);
console.log(newArray); // ["a", "b"]
//可迭代对象
let mySet = new Set([1, 2, 3]);
let setToArray = Array.from(mySet);
console.log(setToArray); // [1, 2, 3]
//String也是可迭代对象
let str = "hello";
let strToArray = Array.from(str);
console.log(strToArray); // ["h", "e", "l", "l", "o"]
//使用映射函数
let arrayLike2 = {
0: 1,
1: 2,
length: 2
};
let newArray2 = Array.from(arrayLike2, (x) => x * 2);
console.log(newArray2); // [2, 4]
- Array.find()
find()
是数组的一个实例方法,用于在数组中查找满足给定条件的第一个元素。如果找到符合条件的元素,就返回该元素;如果遍历完整个数组都没有找到符合条件的元素,则返回undefined
。
参数:
callback必需。这是一个回调函数,用于定义查找条件。它接收三个参数:
element
:当前正在被处理的元素。index
:当前元素的索引(可选参数)。array
:调用find()
方法的原始数组(可选参数)。
thisArg:可选。用于执行callback
函数时的this对象。
javascript
//在一个简单的数字数组中查找第一个大于 10 的元素。
let numbers = [5, 12, 8, 15];
let foundNumber = numbers.find((element) => element > 10);
console.log(foundNumber); // 12
let users = [
{id: 1, name: 'Alice'},
{id: 2, name: 'Bob'},
{id: 3, name: 'Charlie'}
];
let foundUser = users.find((user) => user.id === 2);
console.log(foundUser); // {id: 2, name: "Bob"}
对象的扩展
-
判断两个数据是否相等,底层是通过字符串来判断的
javascript
console.log(NaN == NaN); //如果用等号比,不相等,因为NaN和任何值都不相等
console.log(Object.is(NaN, NaN)); //如果用is方法比,是通过字符串去比,那它俩就相等
- Object.assign()
它将一个或多个源对象的可枚举属性复制到目标对象,并返回目标对象。如果目标对象和源对象有同名属性,那么后面的属性会覆盖前面的属性。
参数
target
:目标对象,是接收属性的对象,必须是对象类型。sources
:一个或多个源对象,是提供属性的对象,这些对象也必须是对象类型。可以有多个源对象,它们的属性会按照顺序依次被复制到目标对象中。
javascript
//简单对象合并,Object.assign()修改了目标对象targetObj,并且返回值也是修改后的目标对象。
let targetObj = {a: 1};
let sourceObj = {b: 2};
let result = Object.assign(targetObj, sourceObj);
console.log(targetObj); // {a: 1, b: 2}
console.log(result); // {a: 1, b: 2}
//多次合并,后面的属性会覆盖前面的。
let obj1 = {a: 1, b: 2};
let obj2 = {b: 3, c: 4};
let obj3 = {d: 5};
let mergedObj = Object.assign({}, obj1, obj2, obj3);
console.log(mergedObj); // {a: 1, b: 3, c: 4, d: 5}
**注意:**该方法的拷贝是浅拷贝。
javascript
let sourceObj = {
nested: {
value: 1
}
};
let targetObj = {};
let result = Object.assign(targetObj, sourceObj);
result.nested.value = 2;
console.log(sourceObj.nested.value); // 2
可以看出,使用拷贝好的对象更改内部值,源对象的内部值也被改变。
Set数据结构
ES6 提供了 新的数据结构 Set。Set 类似于数组 ,但成员的值都是唯一的,没有重复的值。
创建 Set 实例
javascript
let mySet = new Set([1, 2, 3, 3]);
console.log(mySet); // Set(3) {1, 2, 3},注意3只出现了一次,因为Set中的元素是唯一的
Set 的基本操作方法
add
javascript
let set1 = new Set();
set1.add(1);
set1.add(2);
set1.add(1);
console.log(set1); // Set(2) {1, 2},第二次添加1没有效果,因为Set中的元素具有唯一性
delete
javascript
let set2 = new Set([1, 2, 3]);
console.log(set2.delete(2)); // true
console.log(set2); // Set(2) {1, 3}
console.log(set2.delete(4)); // false
has
javascript
let set3 = new Set([5, 6, 7]);
console.log(set3.has(6)); // true
console.log(set3.has(8)); // false
clear
javascript
let set4 = new Set([9, 10, 11]);
set4.clear();
console.log(set4); // Set(0) {}
遍历set
javascript
let set5 = new Set(['a', 'b', 'c']);
for (let key of set5.keys()) {
console.log(key);
}
// 输出:
// a
// b
// c
for (let value of set5.values()) {
console.log(value);
}
// 输出:
// a
// b
// c
for (let [key, value] of set5.entries()) {
console.log(key, value);
}
// 输出:
// a a
// b b
// c c