JS基础
# Javascript
基础
# map
和object
的区别重要
map | object | |
---|---|---|
意外的键 | 默认不包含任何键 | 在原型上可能有与自己冲突的键 |
键的类型 | 可以是任何类型 包括函数 对象 任何基本类型 | 必须是String 或 Symbol |
size | 可以通过size 属性获取 | 手动计算 |
迭代 | 是iterable 可以直接迭代 | 需要获取键再进行迭代 |
性能 | 在频繁增删表现较好 | 频繁增删没有优化 |
# map
和weakMap
、 set
和weakSet
区别
Set
- 成员唯一、无序且不重复
[value, value]
,键值与键名是一致的(或者说只有键值,没有键名)- 可以遍历,方法有:
add
、delete
、has
WeakSet
- 成员都是对象
- 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存
DOM
节点,不容易造成内存泄漏 - 不能遍历,方法有
add
、delete
、has
Map
- 本质上是键值对的集合,类似集合
- 可以遍历,方法很多可以跟各种数据格式转换
WeakMap
- 只接受对象作为键名(
null
除外),不接受其他类型的值作为键名 - 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的(也就是说 一旦不需要
weakmap
里面的键名对象和所对应的键值会自动消失 不用手动删除引用) - 不能遍历,方法有
get
、set
、has
、delete
- 只接受对象作为键名(
# 常见的正则表达式
// (1)匹配 16 进制颜色值
var regex = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;
// (2)匹配日期,如 yyyy-mm-dd 格式
var regex = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;
// (3)匹配 qq 号
var regex = /^[1-9][0-9]{4,10}$/g;
// (4)手机号码正则
var regex = /^1[34578]\d{9}$/g;
// (5)用户名正则
var regex = /^[a-zA-Z\$][a-zA-Z0-9_\$]{4,16}$/;
2
3
4
5
6
7
8
9
10
11
12
13
14
# 介绍一下js
脚本延迟加载的方式特别重要
defer
属性:让脚本加载和文档的解析同步进行 在文档解析完成再执行 使页面的渲染不被阻塞,多个设置defer
会按顺序执行async
属性:解析顺序同defer
属性一样 但是多个设置了async
属性的脚本谁先加载完谁先执行 没有顺序 ,而且不会等文档解析完再执行,容易造成阻塞动态创建
DOM
:对文档的加载事件进行监听 当文档加载完成再动态创建script
引入脚本使用
setTimeout
让
js
最后加载:把js
脚本放在文档最底部
# 常见的类数组转数组方法
Array.prototype.slice.call(arrayLike);
Array.prototype.splice.call(arrayLike, 0);
Array.prototype.concat.apply([], arrayLike);
Array.from(arrayLike);
2
3
4
5
6
7
# 什么是DOM
和BOM
dom
是文档对象类型 将文档当做一个对象 主要处理网页内容的方法和接口bom
是指浏览器对象模型 将浏览器当成一个对象 定义了与浏览器进行交互的方法和接口BOM
的核心对象是window
而window
既是通过js
访问浏览器窗口的接口 又是一个global
对象,存在location
navigator
screen
对象等,最根本的是document
对象
# ES6
模块和CommonJS
模块的异同特别重要
commonjs
的语法是同步的(只有加载完成,才能执行后面的操作) 不适用于浏览器是因为一些文件放在服务器或CDN
上,用同步导入可能时间很长commonjs
是对模块的浅拷贝es6
模块是对模块的引用 换句话说 就是es6
只存只读 不改变他的值(指针指向不变)import
的接口是readonly
,不修改其变量的值commonjs
是运行时加载es6
模块是编译是输出接口- 意思就是
CommonJS
加载的是一个对象(即module.exports
属性),该对象只有在脚本运行完才会生成。而ES6
模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
- 意思就是
加载机制不同
commonjs
遇到循环依赖只会输出已经执行的那部分 后续的输出是不会影响已经输出的变量 而es6
的import
加载一个变量 变量不会被缓存 真正取值的时候才能取到最终的值模块顶层
this
的指向问题cmj
指向当前模块es6
指向undefined
两个模块相互引用
es6
指向加载cmj
cmj
不能require
es6
模块
# export
和module.export
的区别重要
// 基本实现
var module = {
exports: {} // exports 就是个空对象
}
// 这个是为什么 exports 和 module.exports 用法相似的原因
var exports = module.exports
var load = function (module) {
// 导出的东西
var a = 1
module.exports = a
return module.exports
};
2
3
4
5
6
7
8
9
10
11
12
其实就是export
-> 内存 <- module.export
理论上最后导出的还是module.export
如果exports
重新赋值,那就是指向另外的对象
// 🌰
// m1.js
// 1. 正确
module.exports = {
name: 'crucials',
sex: 'boy'
}
// 2. 正确
exports.name = 'crucials';
exports.sex = 'boy'
// 3. 正确
module.exports.name = 'crucials';
module.exports.sex = 'boy'
// 4. 无效
exports = {
name: 'crucials',
sex: 'boy'
}
//可以看到
// exports.name = xxx是module.exports.name = xxx的缩写。
// exports = {}确不是module.exports = {}的缩写。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 还了解AMD
和CMD
吗重要
AMD
方案,这种方案采用异步加载的方式来加载模块,模块的加载不影响后面语句的执行,所有依赖这个模块的语句都定义在一个回调函数里,等到加载完成后再执行回调函数。require.js
实现了AMD
规范CMD
方案,这种方案和AMD
方案都是为了解决异步模块加载的问题,sea.js
实现了CMD
规范。它和require.js
的区别在于模块定义时对依赖的处理不同和对依赖模块的执行时机的处理不同。
# AMD
和 CMD
规范的区别?重要
- 第一个方面是在模块定义时对依赖的处理不同。
AMD
推崇依赖前置,在定义模块的时候就要声明其依赖的模块。而CMD
推崇就近依赖,只有在用到某个模块的时候再去require
。 - 第二个方面是对依赖模块的执行时机处理不同。首先
AMD
和CMD
对于模块的加载方式都是异步加载,不过它们的区别在于模块的执行时机,AMD
在依赖模块加载完成后就直接执行依赖模块,依赖模块的执行顺序和我们书写的顺序不一定一致。而CMD
在依赖模块加载完成后并不执行,只是下载而已,等到所有的依赖模块都加载好后,进入回调函数逻辑,遇到require
语句的时候才执行对应的模块,这样模块的执行顺序就和我们书写的顺序保持一致了。
// AMD 默认推荐
define(["./a", "./b"], function(a, b) {
// 依赖必须一开始就写好
a.doSomething();
// 此处略去 100 行
b.doSomething();
// ...
})
// CMD
define(function(require, exports, module) {
var a = require("./a");
a.doSomething();
// 此处略去 100 行
var b = require("./b"); // 依赖可以就近书写
b.doSomething();
// ...
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
AMD
:requirejs
在推广过程中对模块定义的规范化产出,提前执行,推崇依赖前置CMD
:seajs
在推广过程中对模块定义的规范化产出,延迟执行,推崇依赖就近CommonJs
:模块输出的是一个值的copy
,运行时加载,加载的是一个对象(module.exports
属性),该对象只有在脚本运行完才会生成ES6 Module
:模块输出的是一个值的引用,编译时输出接口,ES6
模块不是对象,它对外接口只是一种静态定义,在代码静态解析阶段就会生成
# 谈谈对模块化开发的理解加分
我对模块的理解是,一个模块是实现一个特定功能的一组方法。在最开始的时候,js
只实现一些简单的功能,所以并没有模块的概念,但随着程序越来越复杂,代码的模块化开发变得越来越重要。
由于函数具有独立作用域的特点,最原始的写法是使用函数来作为模块,几个函数作为一个模块,但是这种方式容易造成全局变量的污染,并且模块间没有联系。
后面提出了对象写法,通过将函数作为一个对象的方法来实现,这样解决了直接使用函数作为模块的一些缺点,但是这种办法会暴露所有的所有的模块成员,外部代码可以修改内部属性的值。
现在最常用的是 立即执行函数 的写法,通过利用闭包来实现模块私有作用域的建立,同时不会对全局作用域造成污染
var module1 = (function(){
var _count = 0;
var m1 = function(){
//...
};
var m2 = function(){
//...
};
return {
m1 : m1,
m2 : m2
};
})();
2
3
4
5
6
7
8
9
10
11
12
13
# use strict
是什么 使用它区别是什么
是es5
一种运行模式 使js
在更严格的条件下运行
目的:
消除
js
语法的不合理 不严谨的地方保证代码运行的安全
提高编译器效率,提高运行速度
区别:
禁止使用
with
语句禁止
this
指向全局对象对象不能有重名
# 如何判断一个对象属于某个类
使用
instanceof
判断构造函数的prototype
是否出现在原型链的任何位置通过
constructor
属性 但是constructor
可以被改写 不安全使用
Object.prototype.toString()
方法来打印对象的[[Class]]
属性来进行判断
# 如何用for...of
遍历对象重要
const obj = {
a: 1,
b: 2,
c: 3
}
for(let i of obj) {
console.log(i)
}
// obj is not iterable
// 法一
const obj = {
a: 1,
b: 2,
c: 3
}
obj[Symbol.iterator] = function() {
const keys = Object.keys(this)
var count = 0
return {
next() {
if(count < keys.length) {
return {value: obj[keys[count++]], done: false}
} else {
return {value: undefined, done: true}
}
}
}
}
for(let i of obj) {
console.log(i)
}
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
# offsetWidth/offsetHeight
,clientWidth/clientHeight
与scrollWidth/scrollHeight
的区别
offsetWidth/offsetHeight
返回值包含content + padding + border
,效果与e.getBoundingClientRect()
相同clientWidth/clientHeight
返回值只包含content + padding
,如果有滚动条,也不包含滚动条scrollWidth/scrollHeight
返回值包含content + padding + 溢出内容的尺寸
# web
开发中会话跟踪的方法有哪些
cookie
session
url
重写- 隐藏
input
ip
地址
# javascript
代码中的"use strict
";是什么意思
use strict
是一种ECMAscript 5
添加的(严格)运行模式,这种模式使得Javascript
在更严格的条件下运行,使JS
编码更加规范化的模式,消除Javascript
语法的一些不合理、不严谨之处,减少一些怪异行为变量必须声明后再使用
函数的参数不能有同名属性,否则报错
不能使用
with
语句不能对只读属性赋值,否则报错
不能使用前缀
0
表示八进制数,否则报错不能删除不可删除的属性,否则报错
不能删除变量
delete prop
,会报错,只能删除属性delete global[prop]
eval
不会在它的外层作用域引入变量eval
和arguments
不能被重新赋值arguments
不会自动反映函数参数的变化不能使用
arguments.callee
不能使用
arguments.caller
禁止
this
指向全局对象不能使用
fn.caller
和fn.arguments
获取函数调用的堆栈增加了保留字(比如
protected
、static
和interface
)
# JSON
的了解
JSON(JavaScript Object Notation)
是一种轻量级的数据交换格式- 它是基于
JavaScript
的一个子集。数据格式简单, 易于读写, 占用带宽小 JSON
字符串转换为JSON
对象:
var obj =eval('('+ str +')');
var obj = str.parseJSON();
var obj = JSON.parse(str);
2
3
JSON
对象转换为JSON
字符串:
var last=obj.toJSONString();
var last=JSON.stringify(obj);
2
# 什么是面向对象编程及面向过程编程,它们的异同和优缺点
- 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了
- 面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为
- 面向对象是以功能来划分问题,而不是步骤
# 谈一谈你理解的函数式编程
- 简单说,"函数式编程"是一种"编程范式"(
programming paradigm
),也就是如何编写程序的方法论 - 它具有以下特性:闭包和高阶函数、惰性计算、递归、函数是"第一等公民"、只用"表达式"
# 异步编程的实现方式
- 回调函数
- 优点:简单、容易理解
- 缺点:不利于维护,代码耦合高
- 事件监听(采用时间驱动模式,取决于某个事件是否发生):
- 优点:容易理解,可以绑定多个事件,每个事件可以指定多个回调函数
- 缺点:事件驱动型,流程不够清晰
- 发布/订阅(观察者模式)
- 类似于事件监听,但是可以通过‘消息中心’,了解现在有多少发布者,多少订阅者
Promise
对象- 优点:可以利用
then
方法,进行链式写法;可以书写错误时的回调函数; - 缺点:编写和理解,相对比较难
- 优点:可以利用
Generator
函数- 优点:函数体内外的数据交换、错误处理机制
- 缺点:流程管理不方便
async
函数- 优点:内置执行器、更好的语义、更广的适用性、返回的是
Promise
、结构清晰。 - 缺点:错误处理机制
- 优点:内置执行器、更好的语义、更广的适用性、返回的是
# 区分什么是“客户区坐标”、“页面坐标”、“屏幕坐标”
- 客户区坐标:鼠标指针在可视区中的水平坐标(
clientX
)和垂直坐标(clientY
) - 页面坐标:鼠标指针在页面布局中的水平坐标(
pageX
)和垂直坐标(pageY
) - 屏幕坐标:设备物理屏幕的水平坐标(
screenX
)和垂直坐标(screenY
)
# 如何删除一个cookie
- 将时间设为当前时间往前一点
var date = new Date();
date.setDate(date.getDate() - 1);//真正的删除
2
3
setDate()
方法用于设置一个月的某一天
expires
的设置
document.cookie = 'user='+ encodeURIComponent('name') + ';expires = ' + new Date(0)
# encodeURI()
、encodeURIComponent()
区别及使用场景
encodeURI()
encodeURI()
通常用于转码整个 URL
,不会对 URL
元字符以及语义字符进行转码
URL
元字符:分号(;
),逗号(,
),斜杠(/
),问号(?
),冒号(:
),at(@
),&
,等号(=
),加号(+
),美元符号($
),井号(#
)
语义字符:a-z
,A-Z
,0-9
,连词号(-
),下划线(_
),点(.
),感叹号(!
),波浪线(~
),星号(*
),单引号('
),圆括号(()
)
encodeURIComponent()
encodeURIComponent()
通常只用于转码URL
组成部分,如URL
中?
后的一串;
如上面的expires
的设置就是转码URL
后面的一串
# 页面编码和被请求的资源编码如果不一致如何处理
- 后端响应头设置
charset
- 前端页面
<meta>
设置charset