Javascript 复习引导
Javascript 解释性语言
特点:
- 动态、头等函数
- 解释一行,执行一行
编译型语言: 一次将全部代码编译可执行的代码,再一行一行执行
ECMA
数据类型:
基本数据类型 (栈:原始数据类型)
number、boolean、string、undefined、null
复杂数据类型 (堆:引用数据类型)
Object
什么是堆?什么是栈?它们之间有什么区别和联系?
堆和栈的概念存在于数据结构中和操作系统内存中。
在数据结构中,栈中数据的存取方式为先进后出。
而堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定。
完全二叉树是堆的一种实现方式。
在操作系统中,内存被分为栈区和堆区。
栈区内存由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆区内存一般由程序员分配释放,若程序员不释放,程序结束时可能由垃圾回收机制回收。
计算
数值计算
- 在进行算数计算时,所有的八进制、十六进制表示的数值都会被转换成十进制。
- 浮点数比较:
【浮点数存储精度丢失】
JavaScript 内部,所有数字都是以64位浮点数形式储存, 按照IEEE754 这个标准映射成一个数字,
这个标准权衡了精度和表示范围, 把64位划分出了3个区域:
区域 S 符号位 用 1 位表示 0表示正数 1表示负数
区域 E 指数位 用 11 位表示 有正负范围,临界值是1023
区域 M 尾数位 用 52 位表示
0.1 内存空间 s(0)E(01111111011)M(1001100110011001100110011001100110011001100110011010)
0.1 转换 精度丢失过程
转换为二进制, 理解精度丢失十分关键的步骤
0.1 => 0.2 => 0.4 => 0.8 => 1.6 => 1.2 => 0.4 => 0.8 => 1.6 =>
1.2 => 0.4 => 0.8 => 1.6 => 1.2 => 0.4 ..............
小数部分不断乘以2,并取整数部分的值,直到小数部分为0为止,应该也是很好理解的,
可以看出这样下去是一个无限循环的过程,转化后是这样子的。
0.00011001100110011001100110011001100110011001100110011001100110011001....
有限空间传入无限的数很明显是不可能, 提取数据,进行数值截取,导致精度丢失
- 加法运算: 只要有一个是字符串,就是字符串拼接。 如果双方都不是string或者number, 先转换数值再转换string进行运算
- 减法运算: 转换成Number
js 中整数的安全范围是多少?
安全整数指的是,在这个范围内的整数转化为二进制存储的时候不会出现精度丢失,能够被“安全”呈现的最大整数是 2^53 - 1,
即9007199254740991,在 ES6 中被定义为 Number.MAX_SAFE_INTEGER。最小整数是-9007199254740991,在 ES6 中
被定义为 Number.MIN_SAFE_INTEGER。
如果某次计算的结果得到了一个超过 JavaScript 数值范围的值,那么这个值会被自动转换为特殊的 Infinity 值。如果某次
计算返回了正或负的 Infinity 值,那么该值将无法参与下一次的计算。判断一个数是不是有穷的,可以使用 isFinite 函数
来判断。
类型比较
undefined == null
NaN !== NaN # Object.is(),解决该问题
null 和 undefined 的区别?
首先 Undefined 和 Null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null。
undefined 代表的含义是未定义,null 代表的含义是空对象。
一般变量声明了但还没有定义的时候会返回 undefined,null
主要用于赋值给一些可能会返回对象的变量,作为初始化。
undefined 在 js 中不是一个保留字,这意味着我们可以使用 undefined 来作为一个变量名,
这样的做法是非常危险的,它会影响我们对 undefined 值的判断。
但是我们可以通过一些方法获得安全的 undefined 值,比如说 void 0。
当我们对两种类型使用 typeof 进行判断的时候,Null 类型化会返回 “object”,这是一个历史遗留的问题。当我们使用双等
号对两种类型的值进行比较时会返回 true,使用三个等号时会返回 false。
undefined 与 undeclared 的区别?
已在作用域中声明但还没有赋值的变量,是 undefined 的。相反,还没有在作用域中声明过的变量,是 undeclared 的。
对于 undeclared 变量的引用,浏览器会报引用错误,如 ReferenceError: b is not defined 。但是我们可以使用 typ
eof 的安全防范机制来避免报错,因为对于 undeclared(或者 not defined )变量,typeof 会返回 "undefined"。
typeof NaN 的结果是什么?
NaN 意指“不是一个数字”(not a number),NaN 是一个“警戒值”(sentinel value,有特殊用途的常规值),用于指出
数字类型中的错误情况,即“执行数学运算没有成功,这是失败后返回的结果”。
typeof NaN; // "number"
关系操作符
- 都是数值,比大小
- 都是字符串,比字符编码值
- 一个是数字,则将另一个转换为数值
switch 默认为全等操作,不会转换
类型转换
数值转换Number()
- Number
- boolean => 1 \ 0
- string => number | NaN。 "" "0" => 0
- null => 0
- undefined => NaN
- 对象,调用valueOf()。 [], ['0'] => 0
- {} => NaN
- symbol Symbol 类型的值不能转换为数字,会报错。
保留小数位:
10.toFixed(2)
===> "10.00" 字符串
转换数字
parseInt(0XAF, 16)
只关注前半部分,空格忽略,必须数字或者负号开头,否则NaN
第二个参数,解析的基数,
转换字符串
undefined & null
.toString() cuocuocuo
String() duiduidui
{} 的 toString 的结果为 "[object Object]", valueOf 结果为 {}
[] 的 toString 的结果为 "", valueOf 结果为 []
String.fromCharCode(104,101,108,108, 111) // hello//接收编码,返回字符串
encodeURI()
encodeURIComponent() //全编
转换Boolean
0 "" undefined null NaN ==> false
- 什么情况下会发生布尔值的隐式强制类型转换?
(1) if (..) 语句中的条件判断表达式。
(2) for ( .. ; .. ; .. ) 语句中的条件判断表达式(第二个)。
(3) while (..) 和 do..while(..) 循环中的条件判断表达式。
(4) ? : 中的条件判断表达式。
(5) 逻辑运算符 ||(逻辑或)和 &&(逻辑与)左边的操作数(作为条件判断表达式)
String
- charCodeAt(idx) 获取指定位置字符的AscII码
- concat()
- slice()
- substring()
- substr() // 从start开始,截取len个字符
- indexOf()
- lastIndexOf()
- trim() //去除前后空白
- trimStart()
- trimEnd()
- replace() //只替换第一个
- search() //支持正则表达式,只返回查找结果的第一个idx
- split()
- toUpperCase()
- toLowerCase()
Object
- .constructor
- .hasOwnProperty(name) //非原型中
- .isPrototypeOf(obj) //传入的对象是否是当前对象的原型
- .getPrototypeOf(obj) //获取原型
- .getOwnPropertyNames(obj) // 获取所有属性名
- .preventExtensions(obj) //不可扩展对象,不能再添加属性
- .seal(obj) // 密封对象,不可扩展且不能删除,但可以改值
- .freeze(obj) //冻结对象,不可扩展、不可删除、不可修改
# Api
"name" in obj // 无论自己还是原型
for in 自己及原型(剔除不可枚举)中的
del [property]
属性类型
Object.defineProperty(obj, atr_name, {xxx})
第三个参数,如果不配置,默认各配置项指定为false
Object.defineProperties()
- 数据属性
- Configurable 能否能删除,默认true
- Enumerable 能否for-in枚举,默认true
- Writeable 能否修改,默认true
- Value 值,默认undefined
- 访问器属性
- Configurable 能否能删除,默认true
- Enumerable 能否for-in枚举,默认true
- get 读取属性时调用,默认undefined
- set 配置属性时调用,默认undefined
读取属性的特性 Object.getOwnPropertyDescriptor()
创建对象的几种方式:
- new Object()
- {}
- Object.create() //指定原型
- 工厂函数创建对象function (xxx, xxx) 中return new Object
- 自定义构造函数, new CustomConstructor(xxx, xxx)
new 关键字解析
- 在内存中创建一个新的空对象 Object.create
- 设置原型,将对象的原型设置为函数的 prototype 对象。Object.create
- this指向这个新对象 constructor.apply
- 执行构造函数,给新对象添加属性、方法 constructor.apply
- 返回这个对象
instanceof
判断构造函数的 prototype 属性(也就是它的原型)是否出现在对象的原型链中的任何位置
object instanceof constructor
object:某个实例对象
constructor:某个构造函数
用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
constructor
F.prototype.constructor === F
f.__proto === F.prototype
Object.getPrototypeOf(f) === F.prototype
Person.prototype = {
constructor: Person, //在原型中指定构造函数
}
This 对象的理解。
this 是执行上下文中的一个属性,它指向最后一次调用这个方法的对象。在实际开发中,this 的指向可以通过四种调用模式来判断。
- 1.第一种是函数调用模式,当一个函数不是一个对象的属性时,直接作为函数来调用时,this 指向全局对象。
- 2.第二种是方法调用模式,如果一个函数作为一个对象的方法来调用时,this 指向这个对象。
- 3.第三种是构造器调用模式,如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。
- 4.第四种是 apply 、 call 和 bind 调用模式,这三个方法都可以显示的指定调用函数的 this 指向。
其中 apply 方法接收两个参数:一个是 this 绑定的对象,一个是参数数组。
call 方法接收的参数,第一个是 this 绑定的对象,后面的其余参数是传入函数执行的参数。
也就是说,在使用 call() 方法时,传递给函数的参数必须逐个列举出来。
bind 方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。
这个函数的 this 指向除了使用 new 时会被改变,其他情况下都不会改变。
这四种方式,使用构造器调用模式的优先级最高,然后是 apply 、 call 和 bind 调用模式,然后是方法调用模式,然后 是函数调用模式。
JavaScript 原型,原型链? 有什么特点?
在 js 中我们是使用构造函数来新建一个对象的,每一个构造函数的内部都有一个 prototype 属性值,这个属性值是一个对
象,这个对象包含了可以由该构造函数的所有实例共享的属性和方法。当我们使用构造函数新建一个对象后,在这个对象的内部
将包含一个指针,这个指针指向构造函数的 prototype 属性对应的值,在 ES5 中这个指针被称为对象的原型。一般来说我们
是不应该能够获取到这个值的,但是现在浏览器中都实现了 __proto__ 属性来让我们访问这个属性,但是我们最好不要使用这
个属性,因为它不是规范中规定的。ES5 中新增了一个 Object.getPrototypeOf() 方法,我们可以通过这个方法来获取对
象的原型。
当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又
会有自己的原型,于是就这样一直找下去,也就是原型链的概念。原型链的尽头一般来说都是 Object.prototype 所以这就
是我们新建的对象为什么能够使用 toString() 等方法的原因。
特点:
JavaScript 对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与
之相关的对象也会继承这一改变。
原型链分析
继承
- 借用构造函数
function Person(name, age) {
this.type = 'human'
this.name = name
this.age = age
}
function Student(name, age) {
Person.call(this, name, age)
}
不能继承原型中的方法
-
拷贝继承(深拷贝)
-
原型继承
Student.prototype = new Person()
函数
- arguments,由实际穿参决定,而非形参决定
- arguments.callee
- func.caller, 调用当前函数的函数的引用。全局是null
function outer() {inner()}
function inner() {inner.caller // outer}
- func.call(thisArg, ...),默认是上层作用域, 扩充函数作用域
func.call(window)
func() {return this.color}
window.color = 'red'
// 改变函数中的this并立即调函数,参数为一个一个传入
- func.apply(thisArg, [argsArray])
// 改变函数中的this并立即调函数,参数为数组一起传入
- func.bind(thisArg, ...)
// 改变函数中的this,并返回一个新函数,不调用函数
// bind之后不能在call或者apply
// 不管bind几次,始终由第一次bind决定,this不能重复绑定
函数柯里化
使用一个闭包返回一个函数,当函数调用时,返回的函数还需要一些传入的参数。
函数柯里化指的是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。
// 普通的add函数
function add(x, y) {
return x + y
}
// Currying后
function curryingAdd(x) {
return function (y) {
return x + y
}
}
add(1, 2) // 3
add(1)(2)
执行环境
执行环境定义了变量或函数有权访问的其他数据。
作用域链
保证执行环境有权访问的所有变量和函数的有序访问,通过作用域链,我们可以访问到外层环境的变量和函数。(函数在执行的过程中,先从自己内部找变量,如果找不到,再从创建当前函数所在的作用域去找, 以此往上)。
作用域链的本质上是一个指向变量对象的指针列表。变量对象是一个包含了执行环境中所有变量和函数的对象。作用域链的前端始终都是当前执行上下文的变量对象。全局执行上下文的变量对象(也就是全局对象)始终是作用域链的最后一个对象。
当我们查找一个变量时,如果当前执行环境中没有找到,我们可以沿着作用域链向后查找。
闭包
内部函数的作用域链中包含外部函数的活动对象。
内部函数从外部函数中被返回后,内部作用域链被初始化包含了外部函数的活动对象和全局变量对象。
外部函数执行完毕后,其活动对象不能被销毁,因为仍有这个活动对象的引用。
由于闭包会携带包含它的函数的作用域,因此会占用更多的内存。
外部无法访问内部的执行环境。
- 坑点1: 引用的变量可能发生变化
function outer() {
var result = [];
for (var i = 0; i<10; i++){
result.[i] = function () {
console.info(i)
}
}
return result
}
// 因为每个闭包函数访问变量i是outer执行环境下的变量i,随着循环的结束,i已经变成10了,
所以执行每个闭包函数,结果打印10, 10, ..., 10
function outer() {
var result = [];
for (var i = 0; i<10; i++){
result.[i] = function (num) {
return function() {
console.info(num);
// 此时访问的num,是上层函数执行环境的num,
数组有10个函数对象,每个对象的执行环境下的number都不一样
}
}(i)
}
return result
}
- 坑点2: this指向问题
var object = {
name: ''object",
getName: function() {
return function() {
console.info(this.name)
}
}
}
object.getName()() // underfined
// 因为里面的闭包函数是在window作用域下执行的,也就是说,this指向windows
- 坑点3:内存泄露问题
function showId() {
var el = document.getElementById("app")
el.onclick = function(){
aler(el.id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
}
}
// 改成下面
function showId() {
var el = document.getElementById("app")
var id = el.id
el.onclick = function(){
aler(id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
}
el = null // 主动释放el
}
(1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,
所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。
解决方法是,在退出函数之前,将不使用的局部变量全部删除。
(2)闭包会在父函数外部,改变父函数内部变量的值。
所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),
把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
延长作用域链:
eval, catch, with
js预解析(变量提升)
- 先提升变量声明到当前作用域的最前面;
- 再提升函数声明到当前作用域的最前面;(所以相同名字函数优先)
数组Array
- 数组的length不是只读的,可用于清空数组
- concat(),返回新数组,不影响原数组
- slice(),返回新数组,[start, end)
- splice(2,0, 'a', 'b') 删除、插入、替换,返回删除掉的项,没删除返回空数组, 改变原数组
- reserve() 改变原数组
- sort() 改变原数组, 默认按照ascii字符代码进行排序
- join()
- flat(depth?) 返回新数组
- flatMap() 等价于 map().flat(1)
- indexOf()
- lastIndexOf()
- find()
- every()
- some()
- filter()
- forEach()
- map() 返回新数组
- reduce(prev, cur, index, array){return prev + cur}
- reduceRight()
- new Array(num) <==> Array(num) 不能map操作 [Empty, Empty, ...]
类数组对象
一个拥有 length 属性和若干索引属性的对象就可以被称为类数组对象,类数组对象和数组类似,但是不能调用数组的方法。
常见的类数组对象有 arguments 和 DOM 方法的返回结果,还有一个函数也可以被看作是类数组对象,因为它含有 length
属性值,代表可接收的参数个数。
常见的类数组转换为数组的方法有这样几种:
(1)通过 call 调用数组的 slice 方法来实现转换
Array.prototype.slice.call(arrayLike);
(2)通过 call 调用数组的 splice 方法来实现转换
Array.prototype.splice.call(arrayLike, 0);
(3)通过 apply 调用数组的 concat 方法来实现转换
Array.prototype.concat.apply([], arrayLike);
(4)通过 Array.from 方法来实现转换
Array.from(arrayLike);
RegExp
- exec('string') 返回第一个匹配项信息的数组。没有的情况下返回null。Array实例有额外两个属性,index表示匹配项在字符串中的位置,input表示应用正则表达式的字符串 -g 每次调用会返回一个匹配项, 没有-g每次调用返回第一个匹配项
- test('') true \ false
元字符
\d \D \w \W \s \S · ^ $
\* + ? {n} {n,} {n,m}
[a-c]匹配 a 至 c 中的任一字符。
[^a-c]匹配除 括号以内的内容
() 分组匹配
str
- .match() // 返回list,如果是/g,返回全部匹配结果
- .matchAll() // 返回所有匹配
- .search() // 始终从头开始找
- .replace() //
Math
- .random() //[0, 1)
- .floor()
- .ceil()
- .abs()
- .max()
- .min()
- .round()
- .power()
- .sqrt()
- .sin()
- .cos()
Error
try {} catch(e) {} finally {}
内存泄漏
哪些操作会造成内存泄漏?
相关知识点:
- 1.意外的全局变量
- 2.被遗忘的计时器或回调函数
- 3.脱离 DOM 的引用
- 4.闭包
回答:
第一种情况是我们由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。
第二种情况是我们设置了 setInterval 定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留
在内存中,而无法被回收。
第三种情况是我们获取一个 DOM 元素的引用,而后面这个元素被删除,由于我们一直保留了对这个元素的引用,所以它也无法被回
收。
第四种情况是不合理的使用闭包,从而导致某些变量一直被留在内存当中。
BOM 浏览器对象模型
window
window.top
window.parent
window.screenLeft
window.screenTop
window.innerWidth
window.innerHeight
location
location.href
- location.protocol
- location.host
- location.port
- location.pathname
- location.search
- location.hash
location.assign() // 可回退,记录
location.replace() // 不可回退,不记录
location.reload() // 重载reload(true)必须从服务器重载
navigator
- .platform
- .userAgent
screen
- .width
- .top
- .left
history
- .go(1 | -1 | 'baidu.com') // 在用户的历史记录中任意跳转
- .back()
- .forward()
- .length
- .pushState()
- .popState()
- .replaceState()
Api
window.open(URL)
const id1 = window.setTimeout()
window.clearTimeout(id1)
const id2 = window.setInterval()
window.clearInterval(id2)
URLSearchParams
定义了一些实用的方法来处理 URL 的查询字符串
- new URLSearchParams('https://example.com?foo=1&bar=2')
- .append(k,v)
- .get(k)
- .delete(k)
- .getAll()
- .set(k,v)
- .has(k)
- .sort()
- .toString()
- .keys()
- .values()
- .entries()
URL
URL() 构造函数返回一个新创建的 URL 对象,表示由一组参数定义的 URL。
如果给定的基本 URL 或生成的 URL 不是有效的 URL 链接,则会抛出一个类型为 SYNTAX_ERROR 的 DOMException。
DOM 文档对象模型
什么是 Virtual DOM?为什么 Virtual DOM 比原生 DOM 快?
我对 Virtual DOM 的理解是,
首先对我们将要插入到文档中的 DOM 树结构进行分析,使用 js 对象将其表示出来,
比如一个元素对象,包含 TagName、props 和 Children 这些属性。
然后我们将这个 js 对象树给保存下来,最后再将 ** DOM 片段** 插入到文档中。
当页面的状态发生改变,我们需要对页面的 DOM 的结构进行调整的时候,
我们首先 根据变更的 状态,重新构建起一棵对象树,
然后将这棵新的对象树和旧的对象树进行比较,记录下两棵树的的差异。
最后将记录的有差异的地方应用到真正的 DOM 树中去,这样视图就更新了。
我认为 Virtual DOM 这种方法对于我们需要有大量的 DOM 操作的时候,
能够很好的提高我们的操作效率,
通过在操作前确定需要做的最小修改,尽可能的减少 DOM 操作带来的重流和重绘的影响。
其实 Virtual DOM 并不一定比我们真实的操作 DOM 要快,
这种方法的目的是为了提高我们开发时的可维护性,
在任意的情况下,都能保证一个尽量小的性能消耗去进行操作。
如何比较两个 DOM 树的差异?
两个树的完全 diff 算法的时间复杂度为 O(n^3) ,
但是在前端中,我们很少会跨层级的移动元素,
所以我们只需要比较同一层级的元素进行比较,这样就可以将算法的时间复杂度降低为 O(n)。
(解释:
首先 DOM 是一个多叉树的结构,如果需要完整的对比两颗树的差异,那么需要的时间复杂度会是 O(n ^ 3),
这个复杂度肯定是不能接受的。于是 React 团队优化了算法,实现了 O(n) 的复杂度来对比差异。
实现 O(n) 复杂度的关键就是只对比同层的节点,而不是跨层对比,
这也是考虑到在实际业务中很少会去跨层的移动 DOM 元素。)
算法首先会对新旧两棵树进行一个深度优先的遍历,会给每个节点添加索引,这样每个节点都会有一个序号。
在深度遍历的时候,每遍历到一个节点,我们就将这个节点 和 新的树中的节点 进行比较,
如果有差异,则将这个差异记录到一个对象中。
(解释:
在第一步算法中我们需要判断新旧节点的 tagName 是否相同,如果不相同的话就代表节点被替换了。
如果没有更改 tagName 的话,就需要判断是否有子元素,有的话就进行第二步算法。
在第二步算法中,我们需要判断原本的列表中是否有节点被移除,
在新的列表中需要判断是否有新的节点加入,还需要判断节点是否有移动。
那么在实际的算法中,我们如何去识别改动的是哪个节点呢?
这就引入了 key 这个属性,想必大家在 Vue 或者 React 的列表中都用过这个属性。
这个属性是用来给每一个节点打标志的,用于判断是否是同一个节点。
当然在判断以上差异的过程中,我们还需要判断节点的属性是否有变化等等。
当我们判断出以上的差异后,就可以把这些差异记录下来。当对比完两棵树以后,就可以通过差异去局部更新 DOM,实现性能的最优化)
在对列表元素进行对比的时候,由于 TagName 是重复的,所以我们不能使用这个来对比。
我们需要给每一个子节点加上一个 key,
列表对比的时候使用 key 来进行比较,这样我们才能够复用老的 DOM 树上的节点。
节点
- .nodeType // 1 元素 2 属性 3 文本 8 注释, 元素节点的nodeType始终是null
- .nodeName
- .nodeValue
- .parentNode
- .childNodes
- .children
- .firstChild
- .lastChild
- .firstElementChild
- .lastElementChild
- .previousSibling
- .nextSibling
- .contains(subNode)// 是否有这个子孙节点
- .removeChild()
- .appendChild()
- .insertBefore(newEle, targetEle) //在指定现有元素之前插入
- .replaceChild()
- .cloneNode()// true 深拷贝
- .innerHTML()
- .innerText()
- .outerHTML()//完全替换调用元素
- .outerText() //完全替换调用元素
- .getAttribute()
- .setAttribute()
- .removeAttribute()
- .querySelector()
- .querySelectorAll()
- .getElementById()
- .getElementsByTagName() //获取不到返回null
- .getElementsByClassName()
- .offsetParent // 获取定位Position的父级元素
- .offsetTop // 边框到顶 只读
- .offsetLeft // 边框到左 只读
- .offsetWidth // border box width 只读
- .offsetHeight // border box height 只读
- .scrollTop // 到content box 顶部滚动区域
- .scrollLeft // 到content box 左侧滚动区域
- .scrollWidth
- .scrollHeight
- .clientLeft // 左边框宽度
- .clientTop // 上边框宽度
- .clientWidth // content box width
- .clientHeight // content box height 只读
- .getBoundingClientRect() // 获取距离页面最顶部、最左部的距离
- .scrollIntoView()
- .getCurrentPosition()
document
document.documentElement
document.body
document.doctype
document.title
document.domain
document.referrer
document.charset
document.write("< p > 111</ p>") //覆盖页面中之前的内容
document.createElement()
document.createTextNode()
document.createDocumentFragment() // 创建文档片段 nodeType 11
document.activeElement // 当前获取了焦点的元素
document.hasFocus() //确定当前文档是否获得了焦点
document.readyState // 文档是否加载完
Unicode 和 UTF-8 之间的关系?
Unicode 是一种字符集合,现在可容纳 100 多万个字符。
每个字符对应一个不同的 Unicode 编码,
它只规定了符号的二进制代码,却没有规定这个二进制代码在计算机中如何编码传输。
UTF-8 是一种对 Unicode 的编码方式,它是一种变长的编码方式,可以用 1~4 个字节来表示一个字符。
创建元素
- document.write("< p > 111</ p>") //覆盖页面中之前的内容
- .innerHTML = "< p > 111</ p>"
- .appendChild( .createElement('div') )
event
- 事件流,事件捕获 1、处于目标 2、事件冒泡3 三个阶段
- 1事件冒泡,事件沿dom树向上传递,在每一级节点上都会发生,知道传播到document
- 3事件捕获,事件沿Dom树向下传递,知道捕获到具体的操作节点,不会触发事件
- onclick="alert('Clicked')"
- .addEventListener('click', function(e){ }, false) //true,在捕获阶段调用事件,false,在冒泡阶段调用事件
- .removeEventListener()
- eventObj
- .target 事件目标(挂载事件)
- .currentTarget 当前正在处理事件的那个元素
- .preventDefault() 取消事件默认行为
- .stopPropagation() 取消事件的进一步捕获或者冒泡
- .eventPhase 调用事件处理的阶段 1, 2, 3
- .bubbles 表明事件是否可以冒泡
- .cancelable 表明是否可以取消默认行为
- .clientX // 客户端位置,视口
- .clientY
- .pageX // 页面位置
- .pageY
- .screenX //全屏幕位置
- .screenY
- .type 事件类型
- load //页面完全加载后,在window上触发
- unload //页面完全卸载后,在window上触发
- resize // 窗口大小调整时,window上触发
- error // js发生错误时,在window上触发
- scroll // 用户滚动滚动条时,触发
- blur // 不会冒泡
- focus //不会冒泡
- focusIn //冒泡
- focusOut // 冒泡
- click
- dblclick
- mousedown
- mouseup
- mouseenter 不冒泡
- mouseleave 不冒泡
- mouseover 冒泡
- mouseout 冒泡
- mousemove
- keydown
- keypress (按下字符键,包括退格键和Esc)
- keyup
- e.keyCode
- textinput //编辑区输入字符
- change //文本改变且失去焦点
- dragstart //被拖放的元素上触发
- drag//被拖放的元素上触发
- dragend//被拖放的元素上触发
- dragenter//作为放置目标的元素上触发
- dragover//作为放置目标的元素上触发
- dragleave//作为放置目标的元素上触发
双击过程
- mousedown、up、click、down、up、click、dblclick
事件性能:
- 事件委托
- 销毁事件 .onclick = null
移动端的点击事件的有延迟,时间是多久,为什么会有? 怎么解决这个延时?
移动端点击有 300ms 的延迟是因为移动端会有双击缩放的这个操作,因此浏览器在 click 之后要等待 300ms,看用户有没有下一次点击,来判断这次操作是不是双击。
有三种办法来解决这个问题:
- 1.通过 meta 标签禁用网页的缩放。
- 2.通过 meta 标签将网页的 viewport 设置为
ideal viewport。 - 3.调用一些 js 库,比如 FastClick
click 延时问题还可能引起点击穿透的问题,就是如果我们在一个元素上注册了 touchStart 的监听事件,这个事件会将这个元素隐藏掉,我们发现当这个元素隐藏后,触发了这个元素下的一个元素的点击事件,这就是点击穿透。
canvas
- toDataURL('xxx.png') //
- !!!!转成图片
Ajax 异步js and xml
- 页面不刷新的情况下,客户端实现局部更新
- 核心
XMLHttpRequestXHR
const xhr = new XMLHttpRequest();
- ActiveXObject("Microsoft.XMLHTTP")
xhr.open(method, url, async) // 默认为异步,并不会真正发送一个请求,而是启动一个请求已备发送。
xhr.timeout
xhr.ontimeout = function() {}
xhr.setRequestHeader()
xhr.send(null) // 参数是请求主体发送数据
xhr.abort() // 取消异步请求
xhr.getRequestHeader()
xhr.getAllResponseHeaders()
-
xhr属性
- .responseText // 作为响应主体被返回的文本 - .responseXML // content-type - .status - .readyState // 响应阶段: 0 未初始化(未open()) 1 启动 (已open未send) // 2 发送 已send未接收到响应 3 接收 已接受到部分响应 4 完成 已接受全部响应 - .onreadystatechange // 事件 - .onload // 接收到完整的响应数据时触发 - .onerror - .onabort - .onloadstart //开始接收第一个字节时触发 - .onloadend //通信完成或者error、abort、load事件后触发 - .onprogress //通信期间频繁触发 - e.totalSize //预期总字节数 - e.position //已接收字节数 - e.lengthComputable// 进度信息是否可用- FormData 表单序列化
- let form = new FormData(); - .append(key, val)- Ajax 解决浏览器缓存问题?
- 1.在 ajax 发送请求前加上 anyAjaxObj.setRequestHeader("If-Modified-Since","0")。 - 2.在 ajax 发送请求前加上 anyAjaxObj.setRequestHeader("Cache-Control","no-cache")。 - 3.在 URL 后面加上一个随机数: "fresh=" + Math.random();。 - 4.在 URL 后面加上时间戳:"nowtime=" + new Date().getTime();。 - 5.如果是使用 jQuery,直接这样就可以了$.ajaxSetup({cache:false})。这样页面的所有 ajax 都会执行这条语句就是不需要保存缓存记录。
fetch()
fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象.
fetch会返回一个Promise作为结果- fetch默认不会带cookie,需要添加配置项
- fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了量的浪费
- fetch没有办法原生监测请求的进度,而XHR可以
- Headers
```
- new Headers({k: v})
- .append(k, v)
- .has(k)
- .get(k)
- .set(k, v)
- .delete(k)
```
- Request
```
- new Request(url, config)
- method - GET, POST, PUT, DELETE, HEAD
- url - URL of the request
- headers - associated Headers object
- referrer - referrer of the request
- mode - cors, no-cors, same-origin
- credentials - should cookies go with the request? omit, same-origin
- redirect - follow, error, manual
- integrity - subresource integrity value
- cache - cache mode (default, reload, no-cache)
```
- Response
```
- new Response(url, data)
- type - basic, cors url
- useFinalURL - Boolean for if url is the final URL
- status - status code (ex: 200, 404, etc.)
- ok - Boolean for successful response (status in the range 200-299)
- statusText - status code (ex: OK)
- headers - Headers object associated with the response.
```
```
clone() - Creates a clone of a Response object.
error() - Returns a new Response object associated with a network error.
redirect() - Creates a new response with a different URL.
arrayBuffer() - Returns a promise that resolves with an ArrayBuffer.
blob() - Returns a promise that resolves with a Blob.
formData() - Returns a promise that resolves with a FormData object.
json() - Returns a promise that resolves with a JSON object.
text() - Returns a promise that resolves with a USVString (text).
```
- 实例
```
var request = new Request('https://davidwalsh.name/users.json', {
method: 'POST',
mode: 'cors',
redirect: 'follow',
headers: new Headers({
'Content-Type': 'text/plain'
})
})
fetch(newRequest).then(function(response) {
// Convert to JSON
return response.json();
}).then(function(j) {
// Yay, `j` is a JavaScript object
console.log(j);
});
```
## localStorage & sessionStorage
- .clear()
- .getItem(name)
- .setItem(name, val)
- .removeItem(name)
## webWorker
- const worker = new Worker("xxx.js")
- worker.postMessage("xxxxx")
- worker.onmessage = (e) => { e.data }
- worker.onerror = e => {}
- worker.terminate(); //立即停止worker工作