块内声明函数
块内声明函数,是指
if(x) {
function foo() {}
}
虽然很多 JS 引擎支持这种写法,但它并不属于 ECMAScript 规范,严格模式下也会报错
ECMAScript 只允许在全局作用域下或函数中声明函数
如果一定要在块内声明函数,可以
if(x) {
var foo = function() {}
}
function *
funciton 跟一个 *, 这种语法会定义一个生成器函数
根据 MDN
- 调用一个生成器函数会返回一个 迭代器对象
- 迭代器对象每次调用 next() 方法,都会执行函数内到下一个 yield 位置为止的语句
- next() 方法返回一个对象,包含两个属性 value 和 done,value 表示本次 yield 返回的值,done 为布尔类型,表示生成器后续是否还有 yield
function* idMaker(){
var index = 0;
while(index<3)
yield index++;
}
var gen = idMaker();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // undefined
- 调用 next() 方法时,如果传入了参数,那么此参数会传给上一条执行的 yield 语句左边的变量。比如
function *gen(){
yield 10;
x=yield 'foo';
yield x;
}
var gen_obj=gen();
console.log(gen_obj.next());// 执行 yield 10,返回 10
console.log(gen_obj.next());// 执行 yield 'foo',返回 'foo'
console.log(gen_obj.next(100));// 将 100 赋给上一条 yield 'foo' 的左值,即执行 x=100,返回 100
console.log(gen_obj.next());// 执行完毕,value 为 undefined,done 为 true
根据知乎,yield 有让出,产出之意。在我看来,有 yield 语句的函数就像一个播放器。可以 pause,可以 play。
正则表达式的 \1
遇到一道题
var str = "Hellllo world";
str = str.replace(/(l)\1/g, '$1');
查了一下,\1 表示第 1 个括号匹配的内容,\2 表示第 2 个括号匹配的内容,依此类推
比如上面代码,相当于 replace(''/(l)l/g', '$1')
,也即每匹配 ll,换成 l
所以结果是
"hello world"
undefined 与 Uncaught Reference Error
一直总错误预测 console.log(不存在的变量) 的执行结果
如下代码
(function() {
var a = 5;
})();
console.log(a);
执行后不是 undefined, 而是 Uncaught Reference Error
一般而言:打印不存在的变量是报错,打印对象不存在的属性是 undefined
注意:打印非对象的属性也是报错,比如 var str = '1111', console.log(str.xx)
null, undefined, false 的 == 关系
null == undefined // true
null == false // false
undefined == false // false
注意到,有 false 的比较式结果为 false
此外,我试了一下五个 falsy 值与 false 的 == 比较运算。发现数字和字符串还是成立的
0 == false // true
'' == false // true
所以:false == falsy 只在 falsy 为数字或字符串类型时成立
不得不说这些题真是坑,虽然花时间多记总能弄懂。但是直接上 === 不好吗?总是考这些平时都不常用的
select 值的获取
如下
<form name="a">
<select name="a" size="1" id="obj">
<option value="a">1</option>
<option value="b">2</option>
<option value="c">3</option>
</select>
对 select 元素 obj,有以下 api 获取选中值信息
obj.value // a or b or c
obj.selectedIndex // 0 or 1 or 2
obj.options // [option元素, option元素, option元素]
// 获取选中元素的值
obj.value
// 获取选中元素的文本
obj.options[obj.selectedIndex].text
let 的变量提升
有一道题
let foo = () => {
console.log(x);
let x = 20;
}
foo();
按照通常的想法,执行顺序应该是
let x
console.log(x)
x = 20
// undefined
但实际是
VM1772:3 Uncaught ReferenceError: Cannot access 'x' before initialization
查了一下,在这里找到答案
所有变量都有创建,初始化和赋值过程
变量必须先创建再初始化,先初始化再赋值
var x 语句:创建变量 x 并初始化为 undefined
let x 语句:创建变量 x,未初始化
let x = 20 语句:创建变量 x 并初始化为 20
所谓变量提升,对 var 是提升创建和初始化,对 let 是提升创建
总的来说,就是:
var x
console.log(x) // ok
let y
console.log(y) // 报错:因为未初始化
forEach 遍历机制2
看一段代码
let array = [,1,,2,,3];
array = array.map((i, j) => j) // 输出什么?
首先,根据之前的博客, forEach 遍历是按成员遍历不是按长度遍历。map 也一样。可以确定,遍历只有 3 次
其次,j 是下标。不确定性在于 1, 2, 3 的下标究竟是 0, 1, 2,还是 1, 3, 5 ?
试着在控制台打印一下
Object.keys(array)
// [1, 3, 5]
因此,结果是 1, 3, 5
这说明,含空的元素数组,成员下标严格按照出现位置
细节
- 字符串也有 concat 方法表示连接,但是没有 append, appendTo, push, pop
- 一个有效变量名可以以字母,下划线开头,不能以数字开头
- 数组的 sort 方法会改变原数组,只要它执行
原地算法
刷力扣的时候,遇到一道题删除排序数组的重复项
里面提到原地算法的概念。所谓原地算法,就是不依赖额外空间或者依赖少数额外空间,仅依靠输出来覆盖输入的一种算法操作。
以前去除数组的重复项,我会用哈希算法。两次遍历,多占用大约 2 倍数组空间(1 倍建表,1 倍构造新数组)
现在学到了一招,就是在数组有序的情况下去重,可用原地算法实现
具体做法就是 双指针法。定义两个指针,一个指向当前元素,一个指向最新元素。在一次遍历覆盖更新当前元素
暂无评论