JS 作用域&作用域链

一个变量的作用域(scope)指的是程序源代码中定义这个变量的区域。
JavaScript只有两种作用域,全局作用域、函数作用域(ES6中新增了块级作用域)。
函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。每一段JavaScript代码(全局代码或者函数)都有一个与之关联的作用域链(scope chain)。这个作用域链是一个对象列表或者链表,这组对象定义了这段代码“作用域中”的变量。当 JavaScript 需要查找变量 x 的值的时候(这个过程被称为“变量解析”(variable resolution)),它会从链中的第一个对象开始查找,如果这个对象有一个名为 x 的属性,则会直接使用这个属性的值,如果第一个对象中不存在名为 x 的属性,JavaScript 会继续查找链上的下一个对象,以此类推。如果作用域链上没有任何一个对象含有属性 x,那么就会抛出一个引用错误(ReferenceError)的异常。

JS 数据类型

js 有哪些数据类型

js有八种数据类型: 数值number(包括整数和浮点数),字符串string,布尔值boolean,未定义undefined,空值null,对象object,符号symbol(ES6添加),大数bigint(ES2020添加)
其中除对象外都是基本数据类型(primitive data type),没有方法,在js中用栈(stack)来存储,对象又被称为引用类型,在js中用堆(heap)来存储。
对象又分为多种 object array function date …
除了 null 和 undefined 之外,所有基本类型都有其对应的包装对象:

Number 为数值基本类型的包装对象。
String 为字符串基本类型的包装对象。
Boolean 为布尔基本类型的包装对象。
Symbol 为字面量基本类型(不支持 new Symbol())的包装对象。
BigInt 为bigint类型(不支持 new BigInt())的包装对象。
一个包装对象的valueOf()方法将会返回它的基本类型值。

阅读更多

JS 笔试题实践

书写一个数组去重的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 利用ES6 Set数据结构去重
function deduplication(array) {
return Array.from(new Set(array));
// return [...new Set(array)];
}

// 遍历一遍 利用对象的属性不能相同的特点去重
function deduplication(array) {
let obj = {}, result = [];
for(let i = 0; i< array.length; i++){
if(!obj[array[i]]){ //如果能查找到,证明数组元素重复了
obj[array[i]] = 1;
result.push(array[i]);
}
}
return result;
};

// 双层遍历,内层遍历改用indexOf
function deduplication(array) {
let result = [];
array.forEach(function(item, index ,arr){ //这里利用map,filter方法也可以实现外层遍历
if(arr.indexOf(item, index + 1) < 0) {
result.push(item);
}
});
return result;
};

// 双层遍历, 遇到重复则跳过,不重复的插入到新数组
function deduplication(array){
var result = [];
for(let i = 0; i < array.length; i++){
for(let j = i + 1; j < array.length; j++){
if(array[i] === array[j]){
j = ++i;
}
}
result.push(array[i]);
}
return result;
}

// 双层遍历,删除重复,返回原数组 *对原数组有修改
function deduplication(array){
let len = array.length;
for(let i = 0; i < len; i++){
for(let j = i + 1; j < len; j++){
if(array[i] == array[j]){
array.splice(j, 1);
len--;
j--;
}
}
}
return array;
};

阅读更多

JS 闭包

什么是闭包

和其他大多数现代编程语言一样,JavaScript也采用词法作用域(lexical scoping)。也就是,函数的执行依赖于变量作用域,这个作用域是函数定义时决定的。而不是函数执行时决定的。为了实现这种词法作用域,JavaScript函数对象的内部状态不仅包含函数的代码逻辑,还必须引用当前的作用域链(scope chain)。函数可以通过作用域链(scope chain)相互关联起来,函数内部的变量都可以保存在函数作用域内,这种特性称为”闭包”。

阅读更多

js 的异步运行机制

我们都知道 JavaScript 是单线程的,但是却很适合做 IO 密集型操作,那么 Javascript 为什么是单线程的又是如何处理异步操作的?下面我们来探究下 Jascript 的异步处理机制。

阅读更多

ES6特性

ES6 是 ECMA 为 JavaScript 制定的第 6 个版本的标准,标准委员会 TC39 最终决定,标准在每年的 6 月份正式发布一次,作为当年的正式版本。ECMAscript 2015 是在2015年6月份发布的ES6的第一个版本。依次类推 ECMAscript 2016 是 ES6 的第二个版本、 ECMAscript 2017 是 ES6 的第三个版本。查看浏览器对ES6的语法支持情况。以下记录了 ES6 的比较常见的特性,并做了简单的解释,默认为 ES2015 引入(finished)的,如果不是则在后面用()标志引入的版本。也可以从这里查看从哪个版本支持

阅读更多

初始化一个从0到99的数组

开发过程中遇到了一个问题,需要mock一些数据。一开始直接用的静态常量,后来想想这实在是太占地方了,尤其为了符合eslint需要写很多行,找其它部分代码的时候也不方便,而且很显然,我们可以用“时间换取空间”,通过程序简化这一部分。
我需要mock的是一个数组,数组里有一些固定的或是随机的数据(这里不妨假设只需要填满不同的数字),那么如何才能简便的mock一个这样的数组呢?

阅读更多

Javascript模块化

模块化

什么是模块化

Javascript在最初的时候没有自己的模块化标准和引入外部模块的方法,类似于 c 的 #include java 的 package ,一个完备的编程语言会考虑一个工程在运行时各个模块的引用和依赖关系。但显然 Javascript 在设计之初只是被当作了一个网页端的脚本语言,既然 HTML 中可以引用js,为什么还要设计 js 中引入 js 的方法呢。可是 Javascript 的发展实在是太快了,我们知道现在 Javascript 不仅在网页中使用,服务器(Nodejs),PC客户端(Electron)甚至移动客户端都有Js的程序。如何更好的定义模块并进行模块间的引用成了Javascript的当务之重。
模块化是一种将系统分离成独立功能部分的方法,可将系统分割成独立的功能部分,严格定义模块接口、模块间具有透明性。

阅读更多

Javascript引擎介绍

Javascript历史

ECMAScript 最初是由网景的 Brendan Eich 开发的一种脚本语言的标准化规范;一开始命名为Mocha,后来改名为LiveScript,最后重命名为JavaScript。1995年12月,Sun 与 Netscape 联合发表了JavaScript。1996年11月,网景公司将 JavaScript 提交给欧洲计算机制造商协会进行标准化。ECMA-262的第一个版本于1997年6月被Ecma组织采纳。ECMA Script是ECMA-262标准化的脚本语言的名称。尽管JavaScript和JScript与ECMAScript兼容,但包含超出ECMA Script的功能。

阅读更多

Javascript中call,apply,bind的作用&模拟实现

call,apply,bind的作用

都是Function的原型方法

Function.prototype.bind()

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

Function.prototype.apply()

apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。

Function.prototype.call()

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

call() 方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。

bind() 方法和call方法的不同之处在于,bind() 方法返回一个新的函数,而call方法会直接调用此函数。

阅读更多

下载csv文件

在B端项目中,经常遇到报表类的需求。即一个表单,然后查询出多种数据,最后导出为excel文件。
这里就需要遇到文件下载类的开发。通常,有以下几种方式:

阅读更多

列表拖拽排序sortablejs

简单的拖拽交换

实现列表拖拽排序,最简单的方法就是将想要拖拽的元素设置为dragable=“true”,然后利用drag事件处理元素位置变换。

拖拽的时候主要有以下几个事件

  • ondragstart 事件:当拖拽元素开始被拖拽的时候触发的事件,此事件作用在被拖曳元素上
  • ondragenter 事件:当拖曳元素进入目标元素的时候触发的事件,此事件作用在目标元素上
  • ondragover 事件:拖拽元素在目标元素上移动的时候触发的事件,此事件作用在目标元素上
  • ondrop 事件:被拖拽的元素在目标元素上同时鼠标放开触发的事件,此事件作用在目标元素上
  • ondragend 事件:当拖拽完成后触发的事件,此事件作用在被拖曳元素上

阅读更多

失效链接替换方案

有些时候,尤其在文档型的网站中,页面链接经常变化调整。通常的做法即搜索网站中的所有相应页面进行url替换,但是即使这样做了,还是有一定风险,用户可能已经收藏了链接,在用户点击收藏链接跳转的时候,会发现页面不存在。这时,我们可以通过设置404页面来引导用户跳转到其它的可能符合用户期望的页面(比如主页)。同样的,如果其它网站引用了该失效链接,同时不希望此网站的引用失效(或者没有修改此网站的权限),就只能在落地页网站做一下兼容,在跳转的时候重新定向一下,具体代码如下:

1
2
3
4
5
6
7
8
!function(){
var path = location.pathname + location.hash;
var urlMap = {
'xxx': 'xxxx',
'xxx/media#live-player': 'xxx/media_live-player/'
};
urlMap[path] && location.replace(urlMap[path]);
}();

这是一种治标不治本的改动,最好的改动还是移除失效的链接。

debounce & throttle

函数节流(throttle)与函数去抖(debounce)

以下场景往往由于事件频繁被触发,因而频繁执行DOM操作、资源加载等重行为,导致UI停顿甚至浏览器崩溃。

  1. window对象的resize、scroll事件

  2. 拖拽时的mousemove事件

  3. 射击游戏中的mousedown、keydown事件

  4. 文字输入、自动完成的keyup事件

实际上对于 window 的 resize 事件,实际需求大多为停止改变大小n毫秒后执行后续处理;而其他事件大多的需求是以一定的频率执行后续处理。针对这两种需求就出现了 debounce 和 throttle 两种解决办法。

阅读更多