初级入门Q&A
整理的一些问题
question | answer |
---|---|
ECMAScript和js的关系 | 前者是后者的规格,后者是前者的其中一种实现(ECMAScript 方言还有Jscript 和ActionScript)。日常场合这两个词是可以互换。 |
node是js的什么 | 是 JavaScript 的服务器运行环境(runtime)。 |
let和var的区别 | let声明的变量,只在let命令所在的代码块有效,let适合用于for。var定义的变量全局只能有一个,且var会发生变量提升(就是变量可以再声明前使用,值为undefined)ps:如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。var x=x和let x=x不同,let会报错 |
声明变量 | var,function,let,const,import,class(还可以通过数组声明如:let [a, b, c] = [1, 2, 3];) |
基本语法
块级作用域
- 有了块级作用域可以再其中声明函数,这种函数类似let声明的变量,作用域以外不可以引用(要避免做这种函数,如果要用,最好写成函数表达式)
- 函数声明语句和函数表达式
-
// 函数声明语句 { let a = 'secret'; function f() { return a; } } // 函数表达式 { let a = 'secret'; let f = function () { return a; }; }
const
const命令一旦声明就要立即初始化,只在声明所在的块级作用域内有效
顶层对象(在浏览器环境指的是window对象,在 Node 指的是global对象。)
window:es5中全局变量就是window对象,但es6为了保持兼容性,var和function声明的是顶层变量,其他声明语句声明的不属于。
global:我不懂!!!!!!!
变量的解构赋值
- set语句的用法:let [x, y, z] = new Set([‘a’, ‘b’, ‘c’]);
x // “a” - 对象的解构赋值:不仅可以用于数组还可以用于对象(但是数组的元素是按次序排列,对象没有次序,且变量必须和属性同名)
-
let { foo, bar } = { foo: "aaa", bar: "bbb" }; foo // "aaa" bar // "bbb"</li>
如果变量和属性不同名
let { foo: baz } = { foo: 'aaa', bar: 'bbb' }; baz // "aaa" let obj = { first: 'hello', last: 'world' }; let { first: f, last: l } = obj; f // 'hello' l // 'world'
- 解析可以用于嵌套结构的对象
- 【一】
let obj = { p: [ 'Hello', { y: 'World' } ] }; let { p: [x, { y }] } = obj; x // "Hello" y // "World"
注意,这时p是模式,不是变量,因此不会被赋值。如果p也要作为变量赋值,可以写成下面这样。
let { p, p: [x, { y }] } = obj;
【二】
ps:另一个例子()const node = { loc: { start: { line: 1, column: 5 } } }; let { loc, loc: { start }, loc: { start: { line }} } = node; line // 1 loc // Object {start: Object} start // Object {line: 1, column: 5}
注意,最后一次对line属性的解构赋值之中,只有line是变量,loc和start都是模式,不是变量。
【三】
嵌套赋值let obj = {}; let arr = []; ({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true }); obj // {prop:123} arr // [true]
【四】
已声明变量用于解构赋值// 错误的写法 let x; {x} = {x: 1}; // SyntaxError: syntax error // 正确的写法 let x; ({x} = {x: 1});
let { log, sin, cos } = Math;
上面代码将Math对象的对数、正弦、余弦三个方法,赋值到对应的变量上,使用起来就会方便很多。
【五】
数组对对象属性的解构let arr = [1, 2, 3]; let {0 : first, [arr.length - 1] : last} = arr; first // 1 last // 3
- 解析可以用于嵌套结构的对象
-
const [a, b, c, d, e] = 'hello'; a // "h" b // "e" c // "l" d // "l" e // "o" let {length : len} = 'hello'; len // 5
- 函数参数的解构赋值
-
[[1, 2], [3, 4]].map(([a, b]) => a + b); // [ 3, 7 ]
function add([x, y]){ return x + y; } add([1, 2]); // 3
- 圆括号问题
- 【三不】
变量声明不能使用let {(x: c)} = {}; let {(x): c} = {};
函数参数不能用
// 报错 function f([(z)]) { return z; } // 报错 function f([z,(x)]) { return x; }
赋值语句的模式不能用
// 全部报错 ({ p: a }) = { p: 42 }; ([a]) = [5];
【可以用】
赋值语句的非模式部分,可以使用圆括号。[(b)] = [3]; // 正确 ({ p: (d) } = {}); // 正确 [(parseInt.prop)] = [3]; // 正确
- 用途
- (1)交换变量的值
let x = 1; let y = 2; [x, y] = [y, x];
(2)从函数返回多个值
// 返回一个数组 function example() { return [1, 2, 3]; } let [a, b, c] = example(); // 返回一个对象 function example() { return { foo: 1, bar: 2 }; } let { foo, bar } = example();
(3)函数参数的定义
// 参数是一组有次序的值 function f([x, y, z]) { ... } f([1, 2, 3]); // 参数是一组无次序的值 function f({x, y, z}) { ... } f({z: 3, y: 2, x: 1});
(4)提取 JSON 数据
let jsonData = { id: 42, status: "OK", data: [867, 5309] }; let { id, status, data: number } = jsonData; console.log(id, status, number); // 42, "OK", [867, 5309]
(5)函数参数的默认值
jQuery.ajax = function (url, { async = true, beforeSend = function () {}, cache = true, complete = function () {}, crossDomain = false, global = true, // ... more config } = {}) { // ... do stuff };
(6)遍历 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) { // ... }
(7)输入模块的指定方法
const { SourceMapConsumer, SourceNode } = require("source-map");
字符串的扩展
【一】Unicode表示法
JavaScript 允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的 Unicode 码点。'\z' === 'z' // true '\172' === 'z' // true '\x7A' === 'z' // true '\u007A' === 'z' // true '\u{7A}' === 'z' // true
【二】codePointAt()
JavaScript 内部,字符以 UTF-16 的格式储存,每个字符固定为2个字节。对于那些需要4个字节储存的字符(Unicode 码点大于0xFFFF的字符),JavaScript 会认为它们是两个字符。
ES6 提供了codePointAt方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点。
codePointAt方法是测试一个字符由两个字节还是由四个字节组成的最简单方法。function is32Bit(c) { return c.codePointAt(0) > 0xFFFF; } is32Bit("?") // true is32Bit("a") // false
【三】String.fromCodePoint()
ES5中用于从码点返回对应字符,但是这个方法不能识别 32 位的 UTF-16 字符(Unicode 编号大于0xFFFF)。
ES6 提供了String.fromCodePoint方法,可以识别大于0xFFFF的字符,弥补了String.fromCharCode方法的不足。在作用上,正好与codePointAt方法相反。String.fromCodePoint(0x20BB7) // "?" String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\uD83D\uDE80y' // true
【四】字符串的遍历器接口
for (let codePoint of 'foo') { console.log(codePoint) } // "f" // "o" // "o"
【五】normalize()
【六】includes(), startsWith(), endsWith()
-includes():返回布尔值,表示是否找到了参数字符串。
-startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
-endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。let s = 'Hello world!'; s.startsWith('Hello') // true s.endsWith('!') // true s.includes('o') // true
支持第二个参数,表示的是开始搜索的位置
let s = 'Hello world!'; s.startsWith('world', 6) // true s.endsWith('Hello', 5) // true s.includes('Hello', 6) // false
【七】repeat()
表示将一个字符串重复多少遍
【八】padStart(),padEnd()'x'.padStart(5, 'ab') // 'ababx' 'x'.padStart(4, 'ab') // 'abax' 'x'.padEnd(5, 'ab') // 'xabab' 'x'.padEnd(4, 'ab') // 'xaba'
padStart()用于头部补全,padEnd()用于尾部补全。
padStart和padEnd一共接受两个参数,第一个参数用来指定字符串的最小长度,第二个参数是用来补全的字符串。
【九】模板字符串
增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。// 普通字符串 `In JavaScript '\n' is a line-feed.` // 多行字符串 `In JavaScript this is not legal.` console.log(`string text line 1 string text line 2`); // 字符串中嵌入变量 let name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?`
如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。
$('#list').html(` <ul> <li>first</li> <li>second</li> </ul> `);
上面代码中,所有模板字符串的空格和换行,都是被保留的,比如
- 标签前面会有一个换行。如果你不想要这个换行,可以使用trim方法消除它。
$('#list').html(` <ul> <li>first</li> <li>second</li> </ul> `.trim());
模板字符串中嵌入变量,需要将变量名写在${}之中。
function authorize(user, action) { if (!user.hasPrivilege(action)) { throw new Error( // 传统写法为 // 'User ' // + user.name // + ' is not authorized to do ' // + action // + '.' `User ${user.name} is not authorized to do ${action}.`); } }
大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。
let x = 1; let y = 2; `${x} + ${y} = ${x + y}` // "1 + 2 = 3" `${x} + ${y * 2} = ${x + y * 2}` // "1 + 4 = 5" let obj = {x: 1, y: 2}; `${obj.x + obj.y}` // "3"
【 十】实例:模板编译:我不懂啊,不懂,过两天重新专门弄一段这个
【十一】标签模板:它可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能function tag(stringArr, value1, value2){ // ... } // 等同于 function tag(stringArr, ...values){ // ... }
tag函数的第一个参数是一个数组,该数组的成员是模板字符串中那些没有变量替换的部分,也就是说,变量替换只发生在数组的第一个成员与第二个成员之间、第二个成员与第三个成员之间,以此类推。
tag函数的其他参数,都是模板字符串各个变量被替换后的值。由于本例中,模板字符串含有两个变量,因此tag会接受到value1和value2两个参数。
tag函数所有参数的实际值如下。
第一个参数:[‘Hello ‘, ’ world ‘, ”]
第二个参数: 15
第三个参数:50
也就是说,tag函数实际上以下面的形式调用。let a = 5; let b = 10; function tag(s, v1, v2) { console.log(s[0]); console.log(s[1]); console.log(s[2]); console.log(v1); console.log(v2); return "OK"; } tag`Hello ${ a + b } world ${ a * b}`; // "Hello " // " world " // "" // 15 // 50 // "OK"
这都是些啥…….
【十二】String.raw()
String.raw方法,往往用来充当模板字符串的处理函数,返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,对应于替换变量后的模板字符串。String.raw({ raw: 'test' }, 0, 1, 2); // 't0e1s2t' // 等同于 String.raw({ raw: ['t','e','s','t'] }, 0, 1, 2);
String.raw = function (strings, ...values) { let output = ''; let index; for (index = 0; index < values.length; index++) { output += strings.raw[index] + values[index]; } output += strings.raw[index] return output; }
【十三】模板字符串的限制
我一定要好好看一眼这个模板是个啥东西,哼(¬︿̫̿¬☆)生气