// ES6新增了块级作用域
// let,const声明的变量
// 函数可以声明在块级作用域内,但由于浏览器兼容性的问题,浏览器的实现可能会不同,应避免使用,可以用下面的方法替代
// 函数声明语句
{
let a = 'secret';
function f() {
return a;
}
}
// 函数表达式替代
{
let a = 'secret';
let f = function () {
return a;
};
}
const 注意
// const实质上保证的是变量指向的内存地址不得改动,如果将对象赋予一个常量,其内容并不能保证为常量
const foo = {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only
const a = [];
a.push('Hello'); // 可执行
a.length = 0; // 可执行
a = ['Dave']; // 报错
//应当使用 Object.freeze 方法将对象冻结
const foo = Object.freeze({});
//冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。
//但是该方法不能保证对象完全不被改变,对象的属性值为对象的情况下依然可以改变
const obj1 = {
internal: {}
};
Object.freeze(obj1);
obj1.internal.a = 'aValue';
obj1.internal.a // 'aValue'
// 要使对象不可变,需要递归地遍历对象的每个属性。但是该方法仍然有冻结不应冻结的对象的风险,例如 window 对象。
// To do so, we use this function.
function deepFreeze(obj) {
// Retrieve the property names defined on obj
var propNames = Object.getOwnPropertyNames(obj);
// Freeze properties before freezing self
propNames.forEach(function(name) {
var prop = obj[name];
// Freeze prop if it is an object
if (typeof prop == 'object' && prop !== null)
deepFreeze(prop);
});
// Freeze self (no-op if already frozen)
return Object.freeze(obj);
}
顶层对象属性
//let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性
var a = 1;
// 如果在Node的REPL环境,可以写成global.a
// 或者采用通用方法,写成this.a
window.a // 1
let b = 1;
window.b // undefined
新的赋值方法
// 解构赋值
// 数组
let [a, b, c] = [1, 2, 3];
let [head, ...tail] = [1, 2, 3, 4];
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
// 对象
let { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
// => baz = {foo:'aaa', bar:'bbb'}.foo
baz // "aaa"
// 字符串,会被转化成一个类数组对象
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
let {length : len} = 'hello';
len // 5
解构模式用途
// 1.变量交换
[x, y] = [y, x];
// 2. 从函数返回多个值,输入模块的指定方法
const { SourceMapConsumer, SourceNode } = require("source-map");
// 3. 提取JSON数据
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
// 4. 函数参数的默认值
jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// ... more config
}) {
// ... do stuff
};
// 5. 便利 map
const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(key + " is " + value);
}
// first is hello
// second is world
// 获取键名
for (let [key] of map) {
// ...
}
// 获取键值
for (let [,value] of map) {
// ...
}
基本扩展
字符串扩展方法
// Unicode编码相关,用到的时候再看
String.fromCharCode,codePointAt,normalize()
// for..of 遍历字符串
let text = String.fromCodePoint(0x20BB7);
for (let i = 0; i < text.length; i++) {
console.log(text[i]);
}
// " "
// " "
for (let i of text) {
console.log(i);
}
// "𠮷"
// 判断一个字符串是否包含在另一个字符串中
let s = 'Hello world!';
s.startsWith('world', 6) // true,从第 n 个位置到字符串结束
s.endsWith('Hello', 5) // true,针对前 n 个字符
s.includes('Hello', 6) // false
// repeat,将原字符串重复几次,返回一个新的字符串
'na'.repeat(2.9) // "nana"
// 使用`来定义,可以传入变量${},{}中甚至可以调用函数,所有的空格缩进都会被保留。
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
// 标签模板——防止 xss,国际化处理
let message =
SaferHTML`<p>${sender} has sent you a message.</p>`;
function SaferHTML(templateData) {
let s = templateData[0];
for (let i = 1; i < arguments.length; i++) {
let arg = String(arguments[i]);
// Escape special characters in the substitution.
s += arg.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
// Don't escape special characters in the template.
s += templateData[i];
}
return s;
}
let sender = '<script>alert("abc")</script>'; // 恶意代码
let message = SaferHTML`<p>${sender} has sent you a message.</p>`;
message
// <p><script>alert("abc")</script> has sent you a message.</p>
i18n`Welcome to ${siteName}, you are visitor number ${visitorNumber}!`
// "欢迎访问xxx,您是第xxxx位访问者!"
// String.raw()内置的模板字符串标签函数,返回一个斜杠被转义(如果已经转义,则不会做任何处理),变量被替换后的模板字符串
String.raw`Hi\n${2+3}!`;
// "Hi\\n5!"
String.raw`Hi\u000A!`;
// 'Hi\\u000A!'
正则相关,用时查
var regex = new RegExp(/xyz/, 'i');
// 四个可以使用正则表达式的方法:match()、replace()、search()和split()
// 为函数的参数设置默认值,通常设置默认值得参数应在参数列表的尾部,以实现可以省略他的目的
function log(x, y = 'World') {
console.log(x, y);
}
function foo(x = 5) {
// 不能在函数体中再次声明 x
// 一个好的代码风格:不论是否函数的参数设置过默认值,在函数体内都不要声明和参数同名的变量
let x = 1; // error
const x = 2; // error
}
// 与解构值配合使用
function foo({x, y = 5} = {}) {
console.log(x, y);
}
// 没有提供参数时会默认传入一个空对象
foo() // undefined 5
---
// 函数的 length 属性:表示该函数预期传入的参数个数
---
// 函数参数的初始化会形成单独的作用域
var x = 1;
function f(x, y = x) {
console.log(y);
}
f(2) // 2
let x = 1;
// 单独形成作用域,作用域中没有找到 x 的值,所以找到原型链上的 x 而不是函数内部的 x
function f(y = x) {
let x = 2;
console.log(y);
}
f() // 1
---
// 利用函数参数的默认值可以显示地制定某一个参数不可省略,省略就抛出错误
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}
foo()
// Error: Missing parameter
// 声明为 undefined 表明参数可以省略
function foo(optional = undefined) { ··· }
---
// rest 参数,取代 arguments,所对应的变量是个数组
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
console.log(item);
});
}
var a = [];
push(a, 1, 2, 3)
---
箭头函数
// 基本用法
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
// 箭头函数的函数体如果多于一条语句,应用{}括起来,且使用 return 返回
var sum = (num1, num2) => { num1++; return num1 + num2; }
// 箭头函数返回对象时需要在对象外面加上括号
let getTempItem = id => ({ id: id, name: "Temp" });
// 和变量解耦配合使用
const full = ({ first, last }) => first + ' ' + last;
// 等同于
function full(person) {
return person.first + ' ' + person.last;
}
---
// this, super, arguments, new.target在箭头函数中都不存在,他们都指向定义时外层函数中对应变量
// 因此,也无法通过 call, apply, bind 改变 this 的指向
---
// 尾调用优化:只保留内部函数的调用帧
function f() {
let m = 1;
let n = 2;
return g(m + n);
}
f();
// 等同于
function f() {
return g(3);
}
f();
// 等同于
g(3);
---
// 尾递归:只保留了一个调用记录,避免的堆栈溢出。只有在严格模式生效,正常模式失效
function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
if( n <= 1 ) {return ac2};
return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}
Fibonacci2(100) // 573147844013817200000
Fibonacci2(1000) // 7.0330367711422765e+208
Fibonacci2(10000) // Infinity
// 尾递归函数改写:确保函数最后一句只调用自身,需要将内部变量变成参数形式。但是这样会很不直观,可以通过下面两种方法规避
// 方法一:currying
function currying(fn, n) {
return function (m) {
return fn.call(this, m, n);
};
}
function tailFactorial(n, total) {
if (n === 1) return total;
return tailFactorial(n - 1, n * total);
}
const factorial = currying(tailFactorial, 1);
factorial(5) // 120
// 方法二:函数默认值
function factorial(n, total = 1) {
if (n === 1) return total;
return factorial(n - 1, n * total);
}
factorial(5) // 120
// 手动实现尾递归优化:减少调用栈 -> 使用‘循环’替换的‘递归’
function tco(f) {
var value;
var active = false;
var accumulated = [];
return function accumulator() {
accumulated.push(arguments);
if (!active) {
active = true;
while (accumulated.length) {
value = f.apply(this, accumulated.shift());
}
active = false;
return value;
}
};
}
var sum = tco(function(x, y) {
if (y > 0) {
return sum(x + 1, y - 1)
}
else {
return x
}
});
sum(1, 100000)
编码规范
// bad
const baz = [...foo].map(bar);
// good
const baz = Array.from(foo, bar);
---
// bad
function processInput(input) {
// then a miracle occurs
return [left, right, top, bottom];
}
// the caller needs to think about the order of return data
const [left, __, top] = processInput(input);
// good
function processInput(input) {
// then a miracle occurs
return { left, right, top, bottom };
}
// the caller selects only the data they need
const { left, top } = processInput(input);
基础快速梳理
新的基本语法
const 注意
顶层对象属性
带来了新的 XSS Payload
数值扩展
进制 0b111 //二进制, 7 0o11 //八进制, 9 0x11 //十六进制, 17
Number 的属性和方法
Math 的属性和方法
数组扩展
编码规范