js基础
基础
数据类型
基本数据类型
Undefined、Null、Boolean、Number 、String
数据封装类对象
Object 、 Array 、 Boolean 、 Number 和 String
其它数据类型
Symbol(定义一个独一无二的值)
symbol使用
let s = Symbol()
let s1 = Symbol(‘foo’);
null表示没有对象,即不应该有值,经常用作函数的参数,或作为原型链的重点。
undefined表示缺少值,即应该有值,但是还没有赋予(变量提升时默认会赋值为undefined,函数参数未提供默认为undefined,函数的返回值默认为undefined)
JSON
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。
它是基于 JavaScript 的一个子集。数据格式简单, 易于读写, 占用带宽小
JSON 字符串转换为 JSON 对象:
var obj =eval('('+ str +')');
var obj = str.parseJSON();
var obj = JSON.parse(str);
JSON 对象转换为 JSON 字符串:
var last=obj.toJSONString();
var last=JSON.stringify(obj);
判断两个对象相等
obj = {a: 1,b: 2}
obj2 = {a: 1,b: 2}
obj3 = {a: 1,b: '2'}
JSON.stringify(obj)==JSON.stringify(obj2);//true
JSON.stringify(obj)==JSON.stringify(obj3);//false
布尔值:
undefined,null, NaN,“”,0, false——>false
null == undefined ------> true
判断数据类型
1.typeof()
“undefined”——如果这个值未定义;
“boolean”——如果这个值是布尔值;
“string”——如果这个值是字符串;
“number”——如果这个值是数值;
“object”——如果这个值是对象或 null;
“function”——如果这个值是函数。
“symbol”——es6新增的symbol类型
typeof(null)—>object
typeof(undefined)---->undefined
typeof(NaN)----->number
2. instanceof
用来判断对象是不是某个构造函数的实例。沿着原型链找。
3.Object.prototype.toString.call()
undefined:[object Undefined]
null:[object Null]
Number :[object Number]
String:[object String]
true:[object Boolean]
[]:[Object Array]
{}:[object Object]
function(){} :[object Function]
4.判断是否是数组
1.Array.isArray(arr)
2.Object.prototype.toString.call(arr) === ‘[Object Array]’
3.arr instanceof Array
4.array.constructor === Array
5.字符串转数字
parseInt()
parseInt(3, 8) -------------------->以八进制为基底将3转为十进制 3
parseInt(3, 2) --------------------> 以二进制为基底将3转为十进制(3不是二进制,报错NaN)
parseInt(3, 0) -------------------->NaN / 报错
6. 数组转字符串,空格分开
str = arr.join(’ ');
splice,slice,concat
slice(start,end) 返回浅拷贝数组 不改变原数组
splice() 如果删除一个元素 返回只包含该元素的数组 原数组改变
concat() 返回拼接后的数组 不改变原数组
数组扁平化
5种方法
递归的遍历每一项,若为数组则继续遍历,否则concat
function flatten(arr) {
var res = [];
arr.map(item => {
if(Array.isArray(item)) {
res = res.concat(flatten(item));
} else {
res.push(item);
}
});
return res;
}
ES6:
function flatten(arr) {
while(arr.some(item=>Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
arr.some():遍历数组中每个元素,判断其是否满足指定函数的指定条件,返回true或者false
数组反转
function reverseArry(arr) [
for(var i = 0;i<arr.length/2;i++){
var temp = arr[i];
arr[i] = arr[arr.length-1-i];
arr[arr.length-1-i] = temp;
}
]
利用unshift()在数组前添加元素
function reverseArry(arr) {
var result = [];
for (let i = 0; i < arr.length; i++){
result.unshift(arr[i])
}
return result;
}
function reverseArray(arr) {
var newArr = [];
for(var i = arr.length-1; i >= 0; i--){
newArr.push(arr[i]);
}
return newArr;
}
function reverseArry(arr) {
var str = arr.join(' '); //数组转字符串,空格分开
var result = [];
var word = '';
for (let i = 0; i < str.length; i++){
if (str[i] != ' '){
word += str[i]; //提取每一组字符串
}
else{
result.unshift(word); //将字符串添加到结果前
word = ''
}
}
result.unshift(word); // 最后一组字符串天剑
return result;
}
eval(将字符串转化成代码执行)
"use strict"
var a = 123;
eval('console.log(a)'); //将字符串转化成代码执行------>123 *只有es5.0*
数组去重
ES6 set
function unique(arr){
return Array.from(new Set(array));
}
splice(ES5)
function unique(arr) {
for (var i = 0; i < arr.length; i++) {
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] == arr[j]) { //第一个等同于第二个, splice 方法删除第二个
arr.splice(j, 1);
j--;
}
}
}
return arr;
}
indexOf
function unique(arr){
if(!Array.isArray){
return false;
}
var array = [];
for(var i = 0; i < arr.length; i++){
if(arr.indexOf(arr[i) == -1){ //返回某个指定的字符串值在字符串中首次出现的位置。如果要检索的字符串值没有出现,则该方法返回 -1。
array.push(arr[i]);
}
}
return array;
}
https://segmentfault.com/a/1190000016418021
Set
ES6 新增的一种新的数据结构,类似于数组,但成员是唯一且无序的,没有重复的值。
数组去重
const s = new Set()
[1, 2, 3, 4, 3, 2, 1].forEach(x => s.add(x))
for (let i of s) {
console.log(i) // 1 2 3 4
}
// 去重数组的重复对象
let arr = [1, 2, 3, 2, 1, 1]
[... new Set(arr)] // [1, 2, 3]
属性,方法
属性:constructor: 构造函数。 size:元素数量
方法:set.**
- add(value):新增,相当于 array里的push
- delete(value):存在即删除集合中value
- has(value):判断集合中是否存在 value
- clear():清空集合
遍历
- keys():返回一个包含集合中所有键的迭代器
- values():返回一个包含集合中所有值得迭代器(默认)
- entries():返回一个包含Set对象中所有元素得键值对迭代器
- forEach(callbackFn, thisArg):用于对集合成员执行callbackFn操作,如果提供了 thisArg 参数,回调中的this会是这个参数,没有返回值
let set = new Set([1, 2, 3])
console.log(set.keys()) // SetIterator {1, 2, 3}
console.log(set.values()) // SetIterator {1, 2, 3}
console.log(set.entries()) // SetIterator {1, 2, 3}
for (let item of set.keys()) {
console.log(item);
} // 1 2 3
for (let item of set.entries()) {
console.log(item);
} // [1, 1] [2, 2] [3, 3]
set.forEach((value, key) => {
console.log(key + ' : ' + value)
}) // 1 : 1 2 : 2 3 : 3
console.log([...set]) // [1, 2, 3]
map, filter
let set = new Set([1, 2, 3])
set = new Set([...set].map(item => item * 2))
console.log([...set]) // [2, 4, 6]
set = new Set([...set].filter(item => (item >= 4)))
console.log([...set]) //[4, 6]
交集,并集,差集
let set1 = new Set([1, 2, 3])
let set2 = new Set([4, 3, 2])
let intersect = new Set([...set1].filter(value => set2.has(value))) //Set {2, 3}
let union = new Set([...set1, ...set2]) //Set {1, 2, 3, 4}
let difference = new Set([...set1].filter(value => !set2.has(value))) //Set {1}
Map(字典)
var,let,const
var
- 可以重复声明,没有报错和警告
- 无法限制修改
- 没有块级作用域, { }
let 和 const
- 不能重复声明
- 都是块级作用域, { } 块内声明的,块外无效
- let 是变量,可以修改。 const 是常量,不能修改
柯里化
先传递一部分参数来调用函数,然后返回一个函数去调用剩下的参数。
把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
// 普通的add函数
function add(x, y) {
return x + y
}
// Currying后
function curryingAdd(x) {
return function (y) {
return x + y
}
}
add(1, 2) // 3
curryingAdd(1)(2) // 3
先用一个函数接收参数x,然后返回一个函数去处理参数y
优点:
参数复用
提前确认(第一参数用来判断是否进行下面操作)
延迟运行
//bind的原生
Function.prototype.bind = function (context) {
var _this = this
//把bind()方法第一个参数以后的所有参数作为返回函数的起始实参
var args = Array.prototype.slice.call(arguments, 1) //arguments是类数组,不是数组.让arguments转换成一个数组对象,让arguments具有slice()方法
将所有传入bind()方法中的实参(第一个参数之后的参数)与this一起绑定
return function() {
return _this.apply(context, args)
}
}
柯里化bind
Function.prototype.bind = function (context) {
let self = this; //此时this指向 test
let arg = Array.prototype.slice.call(arguments, 1);// 去掉第一个,转换成数组
return function () {
let innerArg = Array.prototype.slice.call(arguments);// 此时arguments为传进来的参数,转换成数组
let totalArg = arg.concat(innerArg); // 拼接bind进来的参数与bind之后调用的参数 作为test的参数
return self.apply(context, totalArg);
}
}
new
- 新建了一个空对象
- 对象原型
__proto__
指向构造函数的prototype - 绑定this
- 执行构造函数后返回这个对象
变量提升
将声明提到作用域顶部。
在生成执行环境时,会有两个阶段。
第一个阶段是创建的阶段,JS 解释器会找出需要提升的变量和函数,并且给他们提前在内存中开辟好空间,函数的话会将整个函数存入内存中,变量只声明并且赋值为 undefined 。
第二个阶段,也就是代码执行阶段,我们可以直接提前使用
this指向
- 由 new 调用?绑定到新创建的对象。
- 由 call 或者 apply (或者 bind )调用?绑定到指定的对象。
- 由上下文对象调用?绑定到那个上下文对象。
- 默认:在严格模式下绑定到 undefined ,否则绑定到全局对象。
箭头函数this指向不会改变。
//undefined
(()=>{console.log(this.a)}).apply({a:'word'})
改变this指向
构造函数必须new
call(一个一个传值)
改变this指向,借用别人的函数,实现自己的功能。
Person.call( obj );里面的 call 让 person 所有的 this 都变成 obj。
Person.call(this, name, age, sex);
apply(数组传值)
改变this指向,第2位只能传一个参数arguments。
Person.call(this, [ name, age, sex] );
let name = 'Jack'
const obj = {name: 'Tom'}
function sayHi() {console.log('Hi! ' + this.name)}
sayHi() // Hi! Jack
sayHi.call(obj) // Hi! Tom
bind(返回是函数)
const newFunc = sayHi.bind(obj)
newFunc() // Hi! Tom
caller
function test() {
demo();
}
function demo() {
demo.caller; //demo被调用的环境 -->function test(){ demo(); }
}
test();
说说你对作用域链的理解
- 作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到 window 对象即被终止,作用域链向下访问变量是不被允许的。
- 作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。
原型
概念
原型是 function 对象的一个属性,它定义了构造函数制造出的对象的公共祖先。
通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。
图片转自:https://www.jianshu.com/p/6dd0e22ff63b
函数—>函数:__proto__
:每个对象都由的属性,指向了创建该对象的构造函数的原型
函数—>对象:prototype:是一个指针,指向的是原型对象的内存堆。
对象查看构造函数:constructor
每个对象都有内置的__proto__属性,函数对象才会有prototype属性。
Person.prototype.name 这种.的写法是在原有的基础上把值改了。 改的是属性,也就是
房间里面的东西。
而 Person.prototype={name:’ cherry’ }是把原型改了,换了新的对象。 改了个房间。_proto_指向的空间不变。
原型链__proto__
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。如此层层递进,就构成了实例与原型的链条。
Object.create(原型);
Object.prototype 是原型链的终端
继承
A对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法
先继承,后使用
详情参考:https://www.cnblogs.com/ranyonsue/p/11201730.html
闭包
闭包是指有权访问另一个函数作用域中的变量的函数。
通常你使用只有一个方法的对象的地方,都可以使用闭包。
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
实现计数器
var add = (function(){
var count = 0;
return function(){
return count += 1;
}
})();
add();
用处
- 读取函数内部的变量
- 让变量始终保存在内存中
优点
- 防止变量污染。
- 结果缓存,避免多次调用函数发浪费资源耗时等情况。
缺点
消耗内存,参数和变量不会被垃圾回收机制回收。会造成内存溢出。
内存泄漏
内存泄漏指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放引发的各种问题。
- setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。定时器未清除。
- 闭包使用不当。
- 全局变量
克隆
浅度克隆
遍历obj,将其依次放入obj1。实际上是拷贝地址,改变obj1,obj也变 。
function clone(origin, target) {
var target = target || {}; //如果没定义对象则定义
for (var prop in origin){
target[prop] = origin[prop];
}
return target;
}
clone(obj, obj1)
深度克隆
//遍历对象 for(var prop in obj)
// 1、判断是不是原始值 typeof() ? = obj
// 2、判断数组还是对象 instanceof toString constructor
// 3、建立相应的数组和对象
//递归
function deepClone(obj){
var newObj= obj instanceof Array ? []:{};
for(var item in obj){
var temple= typeof obj[item] == 'object' ? deepClone(obj[item]):obj[item];
newObj[item] = temple;
}
return newObj;
}
CallBack Hell(回调地狱)
由于回调表达异步流程的方式是非线性的、非顺序的,导致代码调试困难。我们需要一种更同步、更顺序、更阻塞的的方式来表达异步,就像我们的大脑一样。
例如: ajax多层嵌套函数,很难处理bug
ajax('XXX1', () => {
// callback 函数体
ajax('XXX2', () => {
// callback 函数体
ajax('XXX3', () => {
// callback 函数体
})
})
})
回调函数:因为javascript是单线程的,所以有些需要等待的地方,需要使用回调函数。
回调地狱:由于某些业务的问题,在代码中会一次性写会多层的回调嵌套,回调嵌套后的代码的维护难度,和无法排除bug。这个就被称作回调地狱。
如何解决:promise、async/await
Promise就是为了解决callback的问题而产生的。
promise、generator、async/await
1. promise
ES6 原生提供了 Promise 对象 ,所谓 Promise,就是一个对象,用来传递异步操作的消息。可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果,可以在对象之间传递和操作promise,帮助我们处理队列。对于开发这种多层嵌套的代码很方便,降低了代码的维护难度等等。
Promise 实现了链式调用,可以传入两个参数: resolve,reject, 分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数,在 then 中 return 的结果会被 Promise.resolve() 包装
优点:解决了回调地狱的问题
缺点:无法取消 Promise ,错误需要通过回调函数来捕获
ajax('XXX1')
.then(res => {
// 操作逻辑
return ajax('XXX2')
}).then(res => {
// 操作逻辑
return ajax('XXX3')
}).then(res => {
// 操作逻辑
})
2. generator
生成器内部的代码是以自然的同步 / 顺序方式表达任务的一系列步骤
function *fetch() {
yield ajax('XXX1', () => {})
yield ajax('XXX2', () => {})
yield ajax('XXX3', () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()
3. async/await
优点:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题
缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。
async function test() {
// 以下代码没有依赖性的话,完全可以使用 Promise.all 的方式
// 如果有依赖性的话,其实就是解决回调地狱的例子了
await fetch('XXX1')
await fetch('XXX2')
await fetch('XXX3')
}
同源策略
同源策略是浏览器上为安全性考虑实施的非常重要的安全策略。
同源:URL的协议、域名和端口相同
。
只允许与本域下的接口交互。目的:避免恶意脚本盗号等。
同源策略的限制:
- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM 和 Js对象无法获得
- AJAX 请求不能发送
跨域
概念
绕过同源策略去获取数据
广义跨域:
- 资源跳转: A链接、重定向、表单提交
- 资源嵌入: <\link>、<\script>、<\img>、<\frame>等dom标签,还有样式background:url()、@font-face()等文件外链
- 脚本请求: js发起的ajax请求、dom和js对象的跨域操作等
核心思想
允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
跨域解决 — JSONP
HTML 中 script,img标签这样获取资源的标签是没有跨域限制的。
JSONP是通过 script 标签加载数据的方式去获取数据当做 JS 代码来执行。
跨域远程获取数据:一般情况下,通过创建<\script>元素,将请求的地址传给src属性。
本地获取数据:callback参数则告诉服务器,我的本地回调函数叫做flightHandler。然后通过本地回调函数,处理数据。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript">
// 得到航班信息查询结果后的回调函数
var flightHandler = function(data){
alert('你查询的航班结果是:票价 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 张。');
};
// 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler";
// 创建script标签,设置其属性
var script = document.createElement('script');
script.setAttribute('src', url);
// 把script标签加入head,此时调用开始
document.getElementsByTagName('head')[0].appendChild(script);
</script>
</head>
<body>
</body>
</html>
ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加。
转载:https://blog.csdn.net/hansexploration/article/details/80314948
跨域解决 —CORS
Cross-Origin Resource Sharing(跨域资源共享 ),是一种 ajax 跨域请求资源的方式。
简单请求:
- 在使用XMLHttpRequest发送请求时,浏览器发现不符合同源策略,会给请求头信息加一个Origin(请求来自哪个源(协议 + 域名 + 端口))
- 服务器根据这个Origin字段是否在许可范围之内,如果在许可范围内,确认接受请求后加一个响应头Access-Control-Allow-Origin。
- 浏览器判断响应头是否有Origin,如果有则处理响应并返回响应数据。
如果没有则被XMLHttpRequest的onerror捕获。
简单跨域请求只需服务器端设置Access-Control-Allow-Origin,带cookie请求的前后端都要设置。
相比于JSONP的优势
JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据
扩展见:http://www.ruanyifeng.com/blog/2016/04/cors.html
同步,异步
异步加载 JS (延迟加载)的方式有哪些?
- defer:并行加载js文件,按照页面上script标签的顺序执行。只支持 IE。
- async : 并行加载js文件,下载完立即执行执行。创建 script,插入到 DOM 中,加载完毕后 callBack。
域名收敛、域名发散
域名收敛
将静态资源放在一个域名下。减少DNS解析的开销。
域名发散
将静态资源放在多个子域名下,就可以多线程下载,提高并行度,使客户端加载静态资源更加迅速。
域名发散是pc端为了利用浏览器的多线程并行下载能力。
域名收敛多用与移动端,提高性能,因为dns解析是是从后向前迭代解析,如果域名过多性能会下降,增加DNS的解析开销。
事件
event.target:返回触发事件的元素
event.currentTarget:返回绑定事件的元素#JavaScript)
事件绑定的方式
- 嵌入dom(DOM1)
<button onclick="func()">按钮</button>
- 直接绑定
btn.onclick = function(){}
- 事件监听(可以冒泡和捕获,DOM2)
btn.addEventListener('click',function(){})
//解绑
btn.removeEventListener('click', function(){})
延伸:DOM3事件
1. UI事件:当用户与页面上的元素交互时触发,如:load、scroll 焦点事件:当元素获得或失去焦点时触发,如:blur、focus
2. 鼠标事件:当用户通过鼠标在页面执行操作时触发如:dbclick、mouseup
3. 滚轮事件:当使用鼠标滚轮或类似设备时触发,如:mousewheel 文本事件:当在文档中输入文本时触发,如:textInput
4. 键盘事件:当用户通过键盘在页面上执行操作时触发,如:keydown、keypress
5. 合成事件:当为IME(输入法编辑器)输入字符时触发,如:compositionstart
6. 变动事件:当底层DOM结构发生变化时触发,如:DOMsubtreeModified
事件流
事件流描述的是从页面中接收事件的顺序
DOM2事件流:
- 事件捕获阶段
- 处于目标阶段
- 事件冒泡阶段
事件冒泡
结构上(非视觉上)嵌套关系的元素,会存在事件冒泡的功能,即同一事件,自子元素冒泡向父元素触发。(自底向上)
阻止冒泡:
W3C:stopPropagation()
IE:cancelBubble = true;
<div class="wrapper">
<div class="content">
<div class="box"></div>
</div>
</div>
<script>
var wrapper = document.getElementsByClassName('wrapper')[0];
var content = document.getElementsByClassName('content')[0];
var box = document.getElementsByClassName('box')[0];
//都是click事件
wrapper.addEventListener('click', function () {
console.log('wrapper')
},false);
content.addEventListener('click', function () {
console.log('content')
},false);
box.addEventListener('click', function () {
console.log('box')
},false);
</script>
事件捕获
父级元素先触发,子级元素后触发。(与冒泡相反)
wrapper,content,box
阻止捕获:
W3C:preventDefault()
IE:window.event.returnValue = false
事件委托
只指定一个事件处理程序,就可以管理某一类型的所有事件。鼠标事件、键盘事件都适合。
把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是 DOM 元素的事件冒泡。使用事件代理的好处是可以提高性能,减少内存占用。
<div class="all">
<div class="box">box1</div>
<div class="box">box2</div>
<div class="box">box3</div>
</div>
document.querySelector('.all').onclick = (event) => {
let target = event.target
if (target.className === 'box') {
console.log(target.innerHTML)
}
}
事件循环
事件循环是一个单线程循环,用于监视调用堆栈并检查是否有工作即将在任务队列中完成。如果调用堆栈为空并且任务队列中有回调函数,则将回调函数出队并推送到调用堆栈中执行。
自定义事件
var event = new Event('build');
// Listen for the event.
elem.addEventListener('build', function (e) { ... }, false);
// Dispatch the event.
elem.dispatchEvent(event);
鼠标事件
mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移除事件是mouseout
mouseenter:当鼠标移除元素本身(不包含元素的子元素)会触发事件,也就是不会冒泡,对应的移除事件是mouseleave
页面某个节点的拖曳
给需要拖拽的节点绑定 mousedown , mousemove , mouseup 事件
mousedown 事件触发后,开始拖拽
mousemove 时,需要通过 event.clientX 和 clientY 获取拖拽位置,并实时更新位置
mouseup 时,拖拽结束
需要注意浏览器边界的情况
防抖,节流
防抖:函数频繁触发,当停止触发后空闲一段时间后执行一次。
function debounce(fn, interval = 300) {
let timeout = null;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, interval);
};
}
节流:指定时间间隔内只会执行一次任务。
function throttle(fn, interval = 300) {
let canRun = true;
return function () {
if (!canRun) return;
canRun = false;
setTimeout(() => {
fn.apply(this, arguments);
canRun = true; //可以执行下一次的循环了
}, interval);
};
}
get,post
get:用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。
post:用于修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get请求适合于请求缓存。
cookie, localstorage, sessionstorage
Cookie
Cookie 是小甜饼的意思。顾名思义,cookie 确实非常小,它的大小限制为4KB左右。它的主要用途有保存登录信息,比如你登录某个网站市场可以看到“记住密码”,这通常就是通过在 Cookie 中存入一段辨别用户身份的数据来实现的。
localStorage
localStorage 是 HTML5 标准中新加入的技术,它并不是什么划时代的新东西。早在 IE 6 时代,就有一个叫 userData 的东西用于本地存储,而当时考虑到浏览器兼容性,更通用的方案是使用 Flash。而如今,localStorage 被大多数浏览器所支持,如果你的网站需要支持 IE6+,那以 userData 作为你的 polyfill 的方案是种不错的选择。
sessionStorage
sessionStorage 与 localStorage 的接口类似,但保存数据的生命周期与 localStorage 不同。做过后端开发的同学应该知道 Session 这个词的意思,直译过来是“会话”。而 sessionStorage 是一个前端的概念,它只是可以将一部分数据在当前会话中保存下来,刷新页面数据依旧存在。但当页面关闭后,sessionStorage 中的数据就会被清空。
特性 | cookie | localstorage | sessionstorage |
由谁初始化 | 客户端或服务器,服务器可以使用Set-Cookie请求头。 | 客户端 | |
易用性 | 需要程序员自己封装,源生的Cookie接口不友好 | 源生接口可以接受,亦可再次封装来对Object和Array有更好的支持 | |
存放数据大小 | 4kb | 5mb | |
与服务器端通信 | 一般由服务器生成,可设置失效时间,如果在浏览器生成,默认是关闭浏览器之后失效 | 永久保存,可清除 | 仅在当前会话有效,关闭页面后清除 |
数据的生命期 | 一般由服务器生成,可设置失效时间。如果在浏览器端生成Cookie,默认是关闭浏览器后失效 | 除非被清除,否则永久保存 | 仅在当前会话下有效,关闭页面或浏览器后被清除 |
访问权限 | 任意界面 | 当前界面 |
重绘、重排
重绘
当一个元素视觉表现属性改变时,会触发重绘。例如元素背景颜色的改变、字体颜色的改变、边框颜色的改变、透明度的改变等。
重排(回流)
当渲染树的一部分或全部更新而导致网页结构或节点尺寸发生改变时,都会导致重排。例如可见元素节点的添加和删除、改变元素的尺寸(元素宽、高、内边距大小、边框大小)、浏览器窗口大小发生改变等。
概念:
通过渲染树中渲染对象的信息,计算出每个渲染对象的位置和尺寸,将其安排在浏览器窗口的正确位置,而有些时候我们会在文档布局完成后对文档布局进行修改,这时候可可能要重新布局,称其“回流”。
每个页面至少需要一次回流,就是在页面第一次加载的时候。
重排一定会重绘
减小重绘和重排的影响
1.改变样式时:
可以使用cssText进行优化 :
//覆盖
element.style.cssText = "border-left:1px;border-right:2px;border-bottom:3px;";
//添加
element.style.cssText += ";border-left:1px;border-right:2px;border-bottom:3px;";
2.批量修改DOM:
(1)使元素脱离文档流(2次重排)
① display:block/none(页面会闪动)
② 使用文档片段(document fragement)在当前DOM之外构建一个子树,再把它拷贝回文档
let fragment = document.createDocumentFragment();
//这里对fragment进行一些节点添加操作
document.querySelector("#mytable").appendChild(fragment);
上述代码触发了一次重排并且只访问了一次DOM
③ 为需要的节点创建一个备份(cloneNode(true),拷贝所有后代,表示深拷贝),然后对副本进行操作,操作完成后,就用新的节点替代旧的节点。
let old = document.querySelector("#mylist");
let clone = old.cloneNode(true);//拷贝所有后代
//这里对clone这个节点进行若干操作
old.parentNode.replaceChild(clone,old);//通过old的父节点找到old然后将old替换成clone
(2)对其应用多重改变。(1次重排)
(3)把元素带回文档中(2次重排)
图片的懒加载和预加载
预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。
懒加载:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。
两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。
懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。
文件断点续传
- 文件识别
文件上传服务器前,本地和服务器“握手”,告诉服务器文件信息和切片大小。 - 文件切片(字节)
var packet = file.slice(start, end);
- 文件传输
本地将每一块文件传输至后台,本地和服务器标识。若文件传输中断可通过标识续传。
垃圾回收
概念
没有被应用的对象就是垃圾,就要被清除。
若几个对象形成一个环,但是根访问不到,也是垃圾。
根(不可删除)
- 本地函数和嵌套调用链上的局部变量和参数
- 全局变量
必要性
由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。
JavaScript的解释器可以检测到何时程序不再需要这个对象,可以把它所占用的内存释放掉了。
var a="hello world";
var b="world";
var a=b; //会释放掉"hello world"
垃圾回收的方法
标记清除
- 获取根,标记。
- 访问标记对象并标记他们的引用
- 以此类推,
- 除标记对象,剩下都删除
virtual dom
用JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中。
当状态变更的时候
- 重新构造一棵新的对象树。
- 新的树和旧的树进行比较,记录两棵树差异
- 把所记录的差异应用到所构建的真正的DOM树上,视图就更新了。
Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。
webpack用来干什么的
webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个bundle。
setTimeout,setInterval
setTimeout(超时调用)
JS有一个任务队列,按顺序执行。setTimeout(fn,1000),1000毫秒后将fn添加到任务队列(若队列为空,立即执行)。
var ID = setTimeout(function(){ //调用setTimeout()之后,该方法会返回一个数值ID,表示超时调用
alert("run");
},1000)
clearTimeout(ID); //取消超时调用
setInterval(间歇调用)
和setTimeout类似,只是要按照指定的时间间隔重复执行代码,直至间歇调用被取消或页面被卸载。
var ID = setInterval(function(){
alert("run");
},1000)
clearInterval(ID);
Event Loop
"Event Loop是一个程序结构,用于等待和发送消息和事件。
- 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
- 当指定的事情完成时,Event Table会将这个函数移入Event Queue。
- 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
- 上述过程会不断重复,也就是常说的Event Loop(事件循环)。
宏任务,微任务
macro-task(宏任务):包括整体代码script,setTimeout,setInterval
micro-task(微任务):Promise,process.nextTick
console.log('script start')
const interval = setInterval(() => { //interval
console.log('setInterval')
}, 0)
setTimeout(() => { //timeout1
console.log('setTimeout 1')
Promise.resolve() //then2
.then(() => console.log('promise 3'))
.then(() => console.log('promise 4'))
.then(() => {
setTimeout(() => { //timeout2
console.log('setTimeout 2')
Promise.resolve().then(() => console.log('promise 5')) //then3
.then(() => console.log('promise 6'))
.then(() => clearInterval(interval))
}, 0)
})
}, 0)
Promise.resolve() //then1
.then(() => console.log('promise 1'))
.then(() => console.log('promise 2'))
- script start,
宏任务 | 微任务 |
---|---|
interval | then1 |
timeout1 |
promise1, promise2
- setInterval
宏任务 | 微任务 |
---|---|
timeout1 | |
interval |
- setTimeout 1
宏任务 | 微任务 |
---|---|
interval | then2 |
interval | |
timeout2 |
promise 3, promise 4
- setInterval , setInterval
宏任务 | 微任务 |
---|---|
timeout2 |
- setTimeout 2
宏任务 | 微任务 |
---|---|
then3 |
promise 5, promise 6