生活还要继续代码撸起来
一、关于定义变量 let和const
//一、let
//1. let 只在let命令所在的代码块内有效
for (let i = 0;i < 5;i++){
var a = i;
let b = i;
}
console.log(a);
//console.log(b);
// 抛出错误 b is not defined
//for 循环中的计数器,很适合使用let
var arr = [];
for(var i = 0; i < 10; i++){
arr[i] = function(){
console.log(i);
}
}
arr[5](); // 输出 10
let arr1 = [];
for(let i = 0; i < 10; i++){
arr1[i] = function(){
console.log(i);
}
}
arr1[5](); // 输出5
// 2、不存在变量提升
function test(){
console.log(t1); // undefined
//console.log(t2); // 抛出错误 b is not defined
var t1 = 0;
let t2 = 1;
}
test();
// 3、暂时性死区
/*
* es6明确规定,如果区域块中存在let和const命令,这个区块对这些命令声明的变量,
* 从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
* 称为“暂时性死区”
* */
var tmp = 123;
if(true){
// tmp = 'abc' //tmp is not defined
let tmp
}
// 4、不允许重复声明变量
/*
* let不允许在相同作用域内,重复声明同一个变量
* */
//二、块级作用域
/*
* 为什么需要块级作用域
* 1、内层变量可能会覆盖外层变量
* 2、用来计数的循环变量泄露为全局变量
* */
for(var k =0; k < 5; k++){}
console.log(k);
// 块级作用域的出现,实际上使得广泛应用的立即执行函数表达式(IIFE)不再必要了
console.log(this)
// 三、const 常量
// const声明一个只读的常量。一旦声明,常量的值就不能改变。
// const实际上保证的,并不是变量的值不得变动,而是变量指向
// 的那个内存地址所保存的数据不得变动。对于简单类型的数据
// (数值、字符串、布尔值),值就保存在变量指向的那个内存地址,
// 因此等同于常量。但对于复合类型的数据(主要是对象和数组)
// ,变量指向的内存地址,保存的只是一个指向实际数据的指针,
// const只能保证这个指针是固定(即总是指向另一个固定的地址)
// ,至于它指向的数据节后是不是可变的,就完全不可能控制了
// 。因此,将一个对象声明为常量必须非常小心。
let 和const注意:
(1) 不存在变量提升,变量在声明之前使用报错;
(2)同一个作用域里,不能重复定义变量;
(3)let 只在let命令所在的代码块(块作用域)内有效;es6明确规定,如果区域块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。称为“暂时性死区”;
二、解构赋值
// 一、数组的解构赋值
//基本用法
/*
* 从数组和对象中提取值,对变量进行赋值,这被称为解构
* 可以从数组中提取值,按照对应位置,对变量赋值
* 如果解构不成功,变量的值就等于undefined
* */
let [a,b,c] = [1,2,3];
// a = 1 b = 2 c = 3
let [i, k] = [1];
// i = 1 k undefined
let [x, y, ...z] = [1,2,3,4];
// x = 1 y = 2 z = [3,4];
let [q,...w] = [1];
// q = 1 w = []
/*
* 默认值
* 解构赋值允许指定默认值 ==> 例1
* 只有当以个数组成员严格等于undefined,默认值才会生效 ==> 例2
* 如果默认值是一个表达式,那么这个表达式是惰性求值的,即使只有在用到手的时候,才会求值。 ==> 例3
* 默认值可以引用解构赋值的其他变量,但该变量必须已经声明。==> 例4
* */
// 例1
let [name , age = 18 ]= ['zsh'];
//name = zsh age = 18
// 例2
let [name1 ,age1 = 18] = ['zsh',null];
//name1 = zsh age = null
// 例3
function f(){
console.log(1)
}
let [o = f()] = [1]; //这种情况下f()不运行
let [t = f()] = []; // 这种情况下f()才会运行
// 例4
let [xx = 1,yy = xx] = [];
// xx = 1 yy= 1
// let [x1 = y1,y1 = 1] = [];
//抛出错误:ReferenceError:y is not defined
// 因为x1用y1做默认值时,y1还没有声明
// 二、对象的解构赋值
/*
* 对象的解构与数组的解构有一个重要的不同。
* 数组的元素是按次序排列的,变量的取值由它的位置决定;
* 而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
* 如果解构失败,变量的值等于undefined
* 嵌套赋值
* 默认值 (同数组)
* */
let { foo,bar } = {foo:'aaa',bar:'bbb'};
// foo = 'aaa' bar = 'bbb'
// 以上写法,是下面形式的简写
// let { foo:foo,bar:bar } = { foo:'aaa',bar:'bbb' };
let {foo1} = {foo:'aaa'};
// fool undefined
// 如果变量名与属性名不一致,必须写成下面之这样
let {foo2:baz} = {foo2 :'bbb'};
// baz = 'bbb' foo2 undefined
// 嵌套赋值例子
let obj = {};
let arr = [];
({foo:obj.prop,bar:arr[0]} = { foo:123,bar:2 });
// obj = {prop: 123} arr = [2]
// 如果要将一个已经声明的变量用于解构赋值,必须要小心
/*
* 错误写法
* let x ;
* { x } = { x : 1 };
* syntaxError:syntax error
* 因为javascript引擎会将{x}理解成一个代码块,从而发生语法错误。
* 只有不将大括号写在行首,避免JavaScript将其解释为代码块,才能解决这个问题。
* 正确写法:
* let x;
* ({x} = {x:1})
* */
// 三、字符串的解构赋值
let [aa,bb,cc,dd,ee] = 'hello';
// aa = h bb = e cc = l dd = l ee = o
// 四、数值和布尔值的解构赋值
/*
* 解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。
* undefined和null无法转为对象
* */
// 五、函数参数的解构赋值
/*
* 通过对这个对象进行解构,得到变量x和y的值。如果解构失败,x和y等于默认值
* undefined就会触发函数参数的默认值。
* */
function move({x = 0,y = 0} = {}){
return [x,y]
}
// move({x:3,y:8}) [3,8]
// move({x:3}) [3,0]
// move({}) [0,0]
// move() [0,0]
// 圆括号问题
// 解构赋值虽然很方便,但是解析起来并不容易。对于编译器来说,
// 一个式子到底是模式,还是表达式,没有办法从一开始就知道,必须解析到(或解析不到)等号才能知道。
//
// 由此带来的问题是,如果模式中出现圆括号怎么处理。ES6 的规则是,只要有可能导致解构的歧义,就不得使用圆括号。
//
// 但是,这条规则实际上不那么容易辨别,处理起来相当麻烦。因此,建议只要有可能,就不要在模式中放置圆括号。
/*
* 以下三种解构赋值不得使用圆括号
*
* (1)、变量声明语句,模式不能使用圆括号
* (2)、函数参数,函数参数也属于变量声明,因此不能带有圆括号
* (3)、赋值语句的模式
* */
/*
* 可以使用圆括号的情况
* 赋值语句的非模式部分,可以使用圆括号
* */
三、字符串扩展
//1、字符的unicode表示法
// 只限于码点在u0000~uFFFF之间的字符
// 2、codePointAt()
// 返回一个字符的码点
// 3、String.formCodePoint()
//从码点返回对应字符,但是这个方法不能识别32位的UTF-16字符
// 4、字符串的遍历器接口 for...of
//5、normalize()
/*
* 6、includes() startsWith() endsWith()
* includes() 返回布尔值,表示是否找到了参数字符串
* startsWith() 返回布尔值,表示参数字符串是否在原字符串的头部
* endsWith() 返回布尔值,表示参数字符串是否在原字符串的尾部
* 三个方法都支持第二个参数,表示开始搜索的位置:
* endsWith的行为与其他两个方法有所不同。他针对前n个字符,
* 而其他两个方法针对从第n个位置知道字符串结束
* */
let str1 = 'Hello world!';
console.log(str1.startsWith('Hello'));
// true
console.log(str1.endsWith('!'));
// true
console.log(str1.includes('o'));
// true
console.log(str1.startsWith('world', 6));
// true
console.log(str1.endsWith('Hello',5));
// true
console.log(str1.includes('Hello',6));
//false
/*
* 7、repeat()
* 返回一个新字符串,表示将原字符串重复n次
* (1)、参数如果是小数会被取整
* (2)、如果参数是负数或者Infinity,会报错
* (3)、0 到 -1之间视为0
* (4)、NAN等同于0
* (5)、参数是字符串,则会先转换成数字
* */
console.log('x'.repeat(5));
// xxxxx
console.log('x'.repeat(2.9));
// xx
/*
* 8、padStart()、padEnd()
* 字符串补全长度,如果某个字符串不够指定长度,会在头部或尾部补全。
* 如果原字符串的长度,等于或大于最大长度,则字符串不全不生效,返回原字符串。
* 如果用来不全的字符串与原字符串,两者的长度之和超过了最大长度,则会截取超出位数的补全字符串。
* 如果省略第二个参数,默认使用空格补全长度。
* 一共接受两个参数,
* 第一个参数是字符串不全时生效的最大长度,
* 第二个参数是用来补全的字符串
* */
// 9、matchAll()方法返回一个正则表达式在当前字符串的所有匹配
/*
* 10、模板字符串 用反引号 ` 标识。
* 模板字符串中嵌入变量,需要将变量名写在${}之中。
* 还可以调用函数${f()}
* ${}里面的变量如果没有声明,会报错
* ${}里面是字符串将会原样输出
* 模板字符串中需要使用反引号,则前面要用反斜杠转义
* 所有的空格和缩进都会被保留在输出之中
* 如果不想要换行可以使用trim()方法消除
*/
let greeting = `\`Yo\` World!`;
console.log(greeting);
//`Yo` World!
let name = 'zsh',time = 'today';
console.log(`Hello ${name},how are you ${time}`)
// Hello zsh,how are you today
let list = `
<ul>
<li>first</li>
<li>second</li>
</ul>
`.trim();
console.log(list);
let oBox = document.getElementsByClassName('box')[0];
oBox.innerHTML = `<ul>
<li>Hello ${name},</li>
<li>how are you ${time}</li>
</ul>`;
// 调用函数
function f(){
return 'f() 函数调用了'
}
console.log(`函数f()返回:${f()}`)
// 嵌套
const tmpl = addrs => `
<table>
${addrs.map(addr => `
<tr><td>${addr.first}</td></tr>
<tr><td>${addr.last}</td></tr>
`).join('')}
</table>
`;
const data = [
{ first: '<Jane>', last: 'Bond' },
{ first: 'Lars', last: '<Croft>' },
];
console.log(tmpl(data));
let a = 5;
let b = 10;
tag`Hello ${ a + b } world ${ a * b }`;
// 等同于
tag(['Hello ', ' world ', ''], 15, 50);
function tag(stringArr, value1, value2){
console.log(arguments)
}
/*
* 11. string.raw()
* 往往用来充当模板字符串的处理函数,返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,
* 对应于替换变量后的模板字符串。String.raw`Hi ${2+3}!`;
* */
String.raw`Hi ${2+3}!`;
// 返回 "Hi\n5!"
String.raw`Hiu000A!`;
// 返回 "Hi\u000A!"