前端笔记整理(JS)

前端基础知识

数据类型

JS数据类型

  • 基本类型:number、string、null、symbol、boolean、undefined
  • 对象类型:Object(Array、RegExp、Math、Map等)、Function

不可以使用 var col = [1,2,] 这样可能会创建2个或者3个长度的数组,因浏览器不同

数组

检测数组
  • ES5新方法:Array.isArray(arr)
    兼容写法:
	function isArray(arr){
		return Object.prototype.toString.call(arr) == "[Object Array]"
	}
  • 只有一个全局执行环境时:arr instanceof Array
迭代方法
  • every((item,index,array)=>{}):所有的数组项都符合判断时返回true,否则返回false;
  • some((item,index,array)=>{}:只要数组项其中一项符合判断时返回true,否则返回false;
  • filter((item,index,array)=>{}:对数组项进行过滤,然后将符合条件的数组项添加到一个新的数组,返回新数组;
  • map((item,index,array)=>{}:遍历且返回执行函数后的结果组成的新数组,返回新数组;
  • forEach((item,index,array)=>{}:仅遍历,不进行返回;
转换方法
  • toString() 返回以逗号拼接的数组各项值的字符串
  • toLocalelString()
  • join(",") 返回以指定字符串拼接的数组各项值的字符串
栈方法(后进先出)
  • push() 接收任意数量的参数,逐个添加至数组末尾,返回修改后的数组的长度
  • pop() 移除数组末尾最后一项,返回移除的项
队列方法(先进先出)
  • shift() 移除数组中的第一项并返回该项
  • unshift() 接收任意数量的参数,逐个添加至数组前端,返回新数组长度
重排序方法
  • reverse():反转数组顺序
  • sort():按升序排列数组项(sort()会调用每个数组项的toString()方法,比较字符串
    sort()可接受一个比较函数作为参数,优化比较
	var arr = [1,5,2,9,11,6];
	arr.sort(function(a,b){
   	return a-b
   })
   console.log(arr)   //[11,9,6,5,2,1]
操作方法
  • concat(a1,a2) 复制一个副本,并且向当前副本添加接收到的参数,可接受任意类型参数(除Object) ,返回新构建的副本
  • slice(start,end) 接收一个或者2个参数。用于截取数组参数,并返回截取到的项组成的新数组
  • splice(start,num,...args) 接收至少2个参数,返回删除的项组成的数组(没有删除时返回[]),用途:
    • 删除:arr.splice(0,2) —— 删除数组前两项
    • 插入:arr.splice(2,0,"dsdg","svsdfg") —— 删除项数量为0,从当前数组的2位置开始添加2项:“dsdg”,“svsdfg”
    • 替换:arr.splice(2,1,"123") —— 向指定位置插入任意数量的项,且同时删除任意数量的项
位置方法(全等比较)
  • indexOf(value[,index]):从数组起始位置查找,返回项在数组中的位置索引
  • lastIndexOf(value[,index]):从数组末尾位置开始查找,返回项在数组中的位置索引

其它:
- ES6中find(value):返回第一个符合传入测试(函数)条件的数组元素;
- findIndex(value):返回符合传入测试(函数)条件的数组元素索引;
- includes(value):判断一个数组是否包含一个指定的值。

归并方法
  • reduce((pre,cur,index,array)=>{}):从第一项开始迭代
  • reduceRight((pre,cur,index,array)=>{}):至少一项返回true,则返回true从最后一项开始迭代
    函数返回的任何值都会自动传递给下一项。第一次迭代发生在数组的第二项上
	var values = [1,3,5,8,9];
	var sum = values.reduce(function(prev,cur,index,arr){
		return prev+cur
	})
	console.log(sum)  //26

类数组(Array-like)对象

slice 方法可以用来将一个类数组(Array-like)对象/集合转换成一个新数组。你只需将该方法绑定到这个对象上。 一个函数中的 arguments 就是一个类数组对象的例子。

function list() {
  return Array.prototype.slice.call(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

简化使用:[].slice.call(arguments)

对象

  • for(let i in obj){}
属性类型

ECMAScript有两种属性:数据属性访问器属性

  1. 数据属性(包含一个数据值的位置):
  • [[Configurable]] :是否可修改属性特性、删除属性、把属性修改为访问器属性(默认为true
  • [[Enumerable]] :是否可以通过for-in循环返回属性(默认为true
  • [[Writable]]:是否可以修改属性值(默认为true
  • [[Value]]:包含这个属性的数据值(默认为undefined

修改属性特性,需要使用ECMAScript5的Object.definedProperty(属性所在对象,属性名,描述符对象)方法,其中描述符必须是数据属性之一。设置一个或者多个值,可以修改对应的特性值
[[Configurable]]设置为false后,则调用Object.definedProperty()方法也无法修改

  1. 访问器属性:
  • [[Configurable]] :是否可修改属性特性、删除属性、把属性修改为访问器属性(默认为true
  • [[Enumerable]] :是否可以通过for-in循环返回属性(默认为true
  • [[Get]]:在读取属性时调用的函数(默认为undefined
  • [[Set]]:在写入属性时调用的函数(默认为undefined

访问器属性不能直接定义,必须使用Object.definedProperty()定义

读取属性的特性(ECMAScript5)

Object.getOwnPropertyDescriptor(属性所在对象,描述符名称)可以取得给定属性的描述符

String类型

  1. 字符串方法:
  • str.charAt(index) 返回给定位置的字符
  • str.charCodeAt(index) 返回给定位置的字符的字符编码
  • ES5中可以使用str[index]访问指定位置的字符
  1. 字符串操作方法
  • concat() 字符串拼接
  • substr(start,num) 返回从起始位置截取num个字符(参数为负值时,-start+字符串长度,-num=0)
  • substring(start,end) 返回从起始位置到结束位置的字符(参数为负值时,全部转为0)
  • slice(start,end) 返回从起始位置到结束位置的字符(参数为负值时,负值+字符串长度进行转换)
  1. 字符串位置方法
  • indexOf(str,start)lastIIndexOf(str,start) 返回字符在字符串中的位置
  1. trim()方法(ES5新提供)—— 删除字符串首尾空格
  2. 字符串大小写转换
  • toLowerCase
  • toUpperCase
  1. 字符串的模式匹配方法
  • match(type) match只接收一个正则或者RegExp对象
    返回数组:
  • serach(type) search只接收一个正则或者RegExp对象
    返回字符串中第一个匹配项的索引
  • replace(type,str|function)
  • split(str,length) 基于一个分隔符将字符串分割成多个字符串并存于一个数组中,可以指定数组长度

类型判断

  • typeof :基本类型的数据中除了null,其它类型都可以通过typeof判断;对于对象类型来说,除function以外,其它类型判断的值均为object;
  • instanceof :通过原型链的方式来判断是否为构建函数的实例,常用于判断具体的对象类型;
  • Object.prototype.toString.call(v) ;
  • isXXX API :isArray、isNaN;
typeof null // object
[] instanceof Array // true
Object.prototype.toString.call(null) // "[object Null]"
Array.isArray([]) // true;  
isNaN(',') // true

nullundefined的区别

  • null是一个表示"无"的对象,转为数值时为0;undefined是一个表示"无"的原始值,转为数值时为NaN

  • undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。典型用法是:

    • 变量被声明了,但没有赋值时,就等于undefined
    • 调用函数时,应该提供的参数没有提供,该参数等于undefined
    • 对象没有赋值的属性,该属性的值为undefined
    • 函数没有返回值时,默认返回undefined
  • null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象。典型用法是:

    • 作为函数的参数,表示该函数的参数不是对象
    • 作为对象原型链的终点。

0.1+0.2 !== 0.3

JS中浮点数用二进制表示的时候是无穷的,因为精度问题,两个浮点数相加会造成截断丢失精度,因此再转换成十进制就会出问题。

手写instanceof

function myInstanceof(obj,pro){
	obj = obj.__prpto__;
	pro = pro.prototype;
	while(obj){
		if(obj === pro) return true;
		obj = obj.__proto__;
	}
	return false;
}

typeof和instanceof类型判断

  • typeof可以准确判断除了null的基本类型,null和对象都会返回object
  • instanceof能准确判断对象的类型,对于基本类型都返回false,内部机制根据原型链来判断,如果沿着A的原型链,同时沿着B的原型链来找,如果能找到同一个引用,就返回true。

类型转换

  • 强制转换:转换成特定的类型(Number()、 v.toString())
    • 转换Boolean规则:undefined、null、false、NaN、0,都转换为false;其它都为true,包括所有对象;
    • 转换Number规则:true为1,false为0;null为0,undefined为NaN ,symbol报错;字符串是数字或者进制值就正常转,否则NaN ,对象隐式转换;
  • 隐式转换:valueOf()、toString() (只有当加法运算符时,有字符串类型则统一转字符串类型,其它只要有数字类型,就都转数字);
[]==![] // -> ?      输出为true
  1. == 和===的区别
  • ==, 两边值类型不同的时候,要先进行类型转换,再比较
  • ===,不做类型转换,类型不同的一定不等;
  1. 特殊数据类型解析
  • Number()、parseIn(value, 基数)、parseFloat()
  • toString(基数)、String()
  • Object:
    • constructor 保存用于创建当前对象的函数
    • hasOwnProperty(name) 检测给定属性在当前对象实例中是否存在
    • isPrototypeOf(object) 检测传入的对象是否是当亲对象的原型
    • propertyIsEnumerable(name) 检查给定的属性是否能够使用 for-in枚举
    • toLocalString() 范湖对象的字符串表示
    • toString()
    • valueOf() 返回对象的字符串、数值或者布尔值表示

toString()和valueof()的区别

  • toString()方法返回一个表示该对象的字符串。如果是对象返回,toString()会返回’[object type]’,其中type就是对象类型;
  • valueof()方法返回指定对象的原始值。JS会利用 valueof()把对象转换为原始类型的值(数值、字符串、布尔值)。
var array = ['aa','bb','cc'];
console.log(array.toString()); //aa,bb,cc
console.log(array.valueOf());//['aa','bb','cc']

深浅拷贝

引用类型复制:将存储在变量对象中的值(指针,指向存储在堆中的一个对象)复制到新变量分配的空间上。复制完成,两个变量将引用同一个对象

  • 两个对象第一层的引用不相同就是浅拷贝的含义。可以通过assign、扩展运算符等方式来实现浅拷贝;
  • 两个对象内部所有的引用都不相同就是深拷贝的含义。可以通过递归的方式解决;
//深拷贝
//1. JSON.stringfy(obj)

//2. 递归

function deepClone(data){
	let type = Object.prototype.toString.call(data);
	let obj;
	if(type === '[object Array]'){
		obj = [];
		for(let i=0;i<data.length;i++){
			obj.deepClone(data[i]);
		}
	}else if(type === '[object Object]'){
		obj = {};
		for(let i in data){
			obj[i] = deepClone(data[i]);
		}
	}else {
		return obj
	}
	return obj
}

变量提升

var声明的变量存在声明提升情况,即提升到变量所在作用域的顶端执行,再到变量位置进行赋值操作;ES6中let声明变量不存在声明提升情况

function test () {
    console.log(a);  //undefined
    var a = 123; 
};

a = 1;
var a;
console.log(a); //1

函数提升

函数提升存在两种方式,即:

  • 函数声明式;该情况是将函数整个代码块提升到它所在的作用域最开始执行;
  • 函数字面量式;同var声明类似,该声明函数只是一个具体的值。

var、let、const的区别

  • var声明的变量,其作用域为该语句所在作用域内,存在变量提升现象;
  • let 声明的变量,其作用域为该语句所在函数内,不存在变量提升现象,存在暂时性死区;且相同作用域下,不可重复定义;
  • const声明的变量声明时必须立即赋值,且不允许修改;

ES6箭头函数

特点:

  • 箭头函数 this为父作用域的 this,不是调用时的this
  • 箭头函数不能作为构造函数,不能使用new
  • 箭头函数么有arguments、caller、callee;
  • 箭头函数通过call和apply 调用,不能改变this的指向,只会传入参数;
  • 箭头函数没有原型属性;
  • 箭头函数返回对象时,要加一个小括号

函数

函数是对象,函数名是指向函数对象的指针。一个函数可以有多个函数名。
函数定义方式:

  • 函数声明 (存在函数声明提升
  • 函数表达式 变量 = 匿名函数

参数

  1. arguments 类数组对象,主要用于保存参数
    arguments 的属性 callee:指针,指向拥有这个arguments对象的函数,递归调用的时候使用arguments.callee(),可以避免因为函数名重复时造成的bug
  2. this 引用的是函数执行的环境对象(全局作用域调用函数时,this引用的是window对象)
  3. callerES5 规范的函数对象属性,保存调用当前函数的函数的引用,可可以使用arguments.callee.caller
    严格模式下,访问arguments.callee()会报错;不能为caller赋值

函数调用过程中,值传递、参数不对等处理上,不管实参的数目大于还是小于形参,数据类型是否准确,调用的函数都会被执行
理解:JS中的参数在内部是用数组表示的。函数接收的始终是个数组(arguments对象),可以通过下标来获取传入的每一个元素(arguments[0]),也可以用length属性来气确定传递进来的参数个数

	function hello(name,msg){
		console.log("hello ' + arguments[0] +arguments[1])
		console.log(arguments.length)
	}
	function say(){
		console.log("hello ' + arguments[0] +arguments[1])
		console.log(arguments.length)
	}
	hellow('lili',',how are you');
	say('lili',',how are you');
	//以上都会输出:hello lili ,how are you

返回值

函数要么有返回值要么没有返回值,否则容易造成调试不便。
未指定返回值的函数返回的是一个特殊值undefined

属性和方法

  1. 每个函数都包含两个非继承而来的方法:apply()call(),两者作用相同,仅仅是接收参数的格式不同
	fun.apply(obj,[...arguments])
	fun.call(obj,...arguments)

作用:

  • 用于在特定的作用域中调用函数,即重新定义函数体内this对象的值
  • 扩充函数作用域,对象不需要与方法有任何的耦合关系,函数中操作使用this指向
  1. ES5 中新定义方法:bind() 创建一个函数的实例,其this值会绑定到传给bind()函数的值

函数重载

     函数没有签名(接收的参数类型和数量),所以ECMAScript没有函数重载。定义相同名称的函数,只会执行最后一个。

递归

定义:一个函数通过名字调用自身

  1. arguments.callee()可以解决函数重定义情况
    arguments.callee()是一个指向正在执行的函数的指针,可用来代替函数名,因此可以用来实现递归调用
  2. 严格模式下arguments.callee()访问时会报错,可用命名函数表达式解决
var factorial = (function f(num){
	if(num < 1){
	return 1
	}else{
	return num*f(num -1)}
})

原型链

this

this对象是指运行时基于函数的执行环境决定的,指向调用函数的对象。全局函数中,this指向window;当函数作为某个对象的方法调用时,this指向那个对象;匿名函数的执行环境具有全局性,其this对象指向window。

  • call()、apply()、bind()会改变this指向,优先级仅次于new;
  • 箭头函数:箭头函数没有this。箭头函数中,this只取决于定义时的环境。
var a = 1
const fn = () => {
  console.log(this.a)
}
const obj = {
  fn,
  a: 2
}
obj.fn()  //1

const a = {
  b: 2,
  foo: function () { console.log(this.b) }
}
function b(foo) {
  // 输出什么?
  foo()
}
b(a.foo)  //undefined

内存泄漏

内存泄漏:指任何对象在不再拥有或需要它之后仍然存在;
操作:

  • setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。
  • 闭包、控制台日志、死循环(在两个对象彼此引用且彼此保留时,就会产生一个循环
  • 过度递归
  • 无效的全局变量

闭包

  • 定义:假如一个函数能访问外部的变量,那么这个函数就是一个闭包。闭包常用来访问私有变量。
  • thisargument对象:与外层函数无关,只与调用自己时的对象以及传的参数有关。
  • 特性:
    • 函数嵌套函数
    • 函数内部可以引用外部的参数和变量
    • 参数和变量不会被垃圾回收机制回收
for (var i = 0; i < 6; i++) {
  setTimeout(() => {
    console.log(i)
  })
}  //输出6  解决办法:let定义i
  1. 数据存放在堆中还是栈中?栈是有结构的,先进后出,数据查询速度:栈>堆
    JS中基本类型值在内存中固定大小,保存在栈内存中;引用类型是对象,保存在堆内存中;JS在定义变量时就分配了内存,使用时候就是对内存的读写操作,内存释放依赖于浏览器的垃圾回收机制;

  2. 垃圾回收

JS每次创建字符串、对象、数组等,解释器都必须动态分配内存来存储实体。这种动态分配了内存的,最终都要释放这些内存一遍他们能够再利用,否则系统内可用内存会被消耗完,造成系统崩溃。

常用回收方法

  • 标记清除;
  • 引用计数
  1. 闭包的优缺点
  • 优点
    • 读取私有变量;
    • 可封装对象的私有属性和私有方法;让这些变量时装保存在内存中;
  • 缺点
    • 占用更多内存;滥用闭包可能会造成网页性能问题,在IE中可能导致内存泄漏(解决办法:退出函数之前,将不适用的局部变量删除

new

  • new操作费、符创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
  • 属性和方法被加入到this 引用的对象中。
  • 新创建的对象由 this 所引用,并且最后隐式的返回 this
//手写实现一个new
// 构造器函数
let Parent = function (name, age) {
    this.name = name;
    this.age = age;
};
Parent.prototype.sayName = function () {
    console.log(this.name);
};
//自己定义的new方法
let newMethod = function (Parent, ...rest) {
    // 1.以构造器的prototype属性为原型,创建新对象;
    let child = Object.create(Parent.prototype);
    // 2.将this和调用参数传给构造器执行
    let result = Parent.apply(child, rest);
    // 3.如果构造器没有手动返回对象,则返回第一步的对象
    return typeof result  === 'object' ? result : child;
};
//创建实例,将构造函数Parent与形参作为参数传入
const child = newMethod(Parent, 'echo', 26);
child.sayName() //'echo';

作用域

定义:变量的可访问性。
分类:

  • 全局作用域
  • 函数作用域
  • 块级作用域(ES6中的let、const)

作用域链:作用域的嵌套。往上遍历查找。

问:箭头函数和普通函数的区别

  • 箭头函数的 this 永远指向其上下文的 this ,任何方法都改变不了其指向,如 call() , bind() , apply()
  • 普通函数的this指向调用它的那个对象

作用域链

作用域链本质上是一个指向变量对象的指针列表,只是引用但不实际包含变量对象。

模仿块级作用域

JavaScript没有块级作用域的概念。js可多次声明同一变量,可多次执行初始化。匿名函数可以用来模仿块级作用域,限制向全局作用域中添加过多的变量和函数

	;(function(){
		//块级作用域
	})();

原型

原型是什么?

  • 所有对象都有一个属性 __proto__指向一个对象,也就是原型;
  • 每个对象的原型可以通过 constructor找到构造函数,沟站函数也可以通过prototype找到原型;
  • 对象之间通过__proto__连接起来,这样称之为原型链;当前对象上不存在的属性可以通过原型链一层层往上查找,直到顶层Object对象,最后就是null

继承

即使是ES6中的class也不是其它语言中的类,本质就是一个函数。

class Person {};
Person instanceof Function  //true

ES6和ES5中的继承的区别:

  • ES6 继承的子类需要调用 super() 才能拿到子类,ES5 的话是通过 apply 这种绑定的方式
  • 类声明不会提升,和 let 这些一致
  1. JS 如何实现继承
    JS实现继承主要是依靠原型链。主要有6种继承方式:
  • 原型链:利用原型让一个引用类型继承另外一个引用类型的属性和方法;
  • 借用构造函数:在子类型构造函数的内部调用超类构造函数,通过使用call()和apply()方法可以在新创建的对象上执行构造函数;
function SuperType() {
	this.colors = ["red","blue","green"];
}
function SubType() {
	SuperType.call(this);//继承了SuperType
}

var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors);//"red","blue","green","black"

var instance2 = new SubType();
console.log(instance2.colors);//"red","blue","green"
  • 组合继承
  • 原型式继承
  • 寄生式继承
  • 寄生组合市继承
  1. 通过原型实现的继承和class有什么区别?

类从类继承并创建子类关系;原型是一个工作对象实例。对象直接从其他对象继承。

单体内置对象

Global对象

所有在全局作用域中定义的属性和函数,都是Global对象的属性和方法

  1. URI 编码方法
    encodeURI()encodeURIComponent()decodeURI()decodeURIComponent()
  2. evval(str)方法

eval() 类似一个JavaScript解析器,只接收一个参数(要执行的javascript字符串)

  • eval()中定义的变量和函数都不会被提升;只在eval()执行的时候创建
  • 严格模式下,在外部无法访问eval()中定义的任何变量或者函数
  • 使用eval(),存在代码注入的恶意攻击危险
  1. window对象
    在全局作用域中声明的变量和函数,都成为了window对象的属性

Math对象

  1. min()max()方法
    可接收任意多个数值参数。求数组中的最大值方法
	//将Math对象作为apply的第一个参数,从而正确设置this的值
	var maxValue = Math.max.apply(Math,arr)
  1. 舍入方法
  • Math.ceil() 向上舍入
  • Math.floor() 向下舍入
  • Math.round() 标准舍入
  1. Math.random() 方法
    用于返回一个0~1之间的随机数
  2. 其它 方法
  • Math.abs(num) 求绝对值
  • Math.sqrt(num) 求平方根
  • Math.pow(num,power) 求power次幂

Date类型

  • Date.parse() 因地区而异。接收一个表示日期的字符串参数,尝试返回响应的日期毫秒数(字符串不能表示日期的,则返回NaN
  • Date.UTC(年,月,日,时,分,秒) 基于本地时间创建。接收多个参数,尝试返回表示日期的毫秒数
  • Date.now() 返回调用这个方式时的日期和时间的毫秒数(ES5 新提供的
    在不支持Date.now() 的浏览器中,可以使用+new Date() 达到相同的效果

继承的方法

toString()toLocalelString()valueOf() 不同浏览器返回的不同,基本用于测试而已。

日期格式化方法

  • toDateString()toTimeString()
  • toLocaleDateString()toLocaleTimeString()
  • toUTCString()

arguments类数组转换为数组的方法

arguments是一个类数组对象,存储的是当前传入函数参数的个数,具有有length属性。但arguments并不是真正的数组,下边是两种常用的将其转换为真正的数组的方法。

var args = Array . prototype . slice . call ( arguments );

var args=[];
for(var i=1;i<arguments.length;i++){
    args.push(arguments[i]); 

语句

for-in语句

迭代语言,枚举对象属性
for(var property in object){ }

  1. 所有对象属性都被返回一次,但是先后次序可能会因为浏览器不同而有差异
  2. 要迭代的对象的变量值为null或者undefined,for-in不执行循环体。所以使用for-in之前,建议先检测确定改对象的值;

label语句

使用label可以在diamante中添加标签

var str = '';
start:
	for(var i = 0;i < 10;i++){
		if(i == 5)
			continue start;
		str+= i;
	}
console.log(str) //012346789

breakcontinue语句

  • break立即退出循环,强制继续执行循环体后的代码
  • continue立即退出循环,从循环顶部继续执行

switch语句

  • switch语句比较值时用的是全等操作符
  • 可使用多种数据类型

执行上下文

执行上下文:指当前执行环境中的变量、函数声明,参数(arguments),作用域链,this等信息。
javascript运行的代码环境有三种:

  • 全局代码:代码默认运行的环境,最先会进入到全局环境中
  • 函数代码:在函数的局部环境中运行的代码
  • Eval代码:在Eval()函数中运行的代码

全局上下文是最外围的一个执行环境,web浏览器中被认为是window对象。在初始化代码时会先进入全局上下文中,每当一个函数被调用时就会为该函数创建一个执行上下文,每个函数都有自己的执行上下文。

const ExecutionContextObj = {
    VO: window, // 变量对象
    ScopeChain: {}, // 作用域链
    this: window
};

执行上下文生命周期

  • 创建阶段
    • 生成变量对象
    • 创建arguments
    • 扫描函数声明
    • 扫描变量声明
    • 建立作用域链
    • 确定this的指向
  • 执行阶段
    • 变量赋值
    • 函数的引用
    • 执行其他代码

Promise

Promise是异步编程的一种解决方案。对象的状态不受外界的影响,并且状态改变后就不会再变(成功/失败);

  • 3种状态:Pending(进行中)、Fulfilled(已成功)、Rejected(已失败)
  • 两种变化:pending->Fulfilled、Pending->Rejected
  • 优点:解决回调,减少嵌套
  • 缺点:无法监听进行状态;内部错误无法抛出;新建立即执行且无法取消

使用:

//创建Promise实例
let promise = new Promise((resolve,reject)=>{
	if(/*异步操作成功*/){
		resolve(value);
	}else{
		reject(error)
	}
})
promise.then(res=>{
	//执行成功操作
})
  1. 使用all实现并行需求
function TaskWaitAll() {
	  function timerPromisefy(func) {
	       return new Promise(function (resolve, reject) {
	           func(resolve, reject);
	       });
	   }
	   var taskall = [];
	   for (var i = 0; i < arguments.length; i++) {
	       taskall.push(timerPromisefy(arguments[i]));
	   }
	   return Promise.all(taskall);
}
TaskWaitAll(function (resolve) {
   resolve(1);
},function (resolve) {
    resolve(2);
},function (resolve) {
    resolve(3);
}).then(function (results){
    console.log(results);//打印出来等于[1,2,3]
});
  1. 手写all的实现
Promise.prototype.all = function (iterators) {
    const promises = Array.from(iterators)
    const promiseList = [],
        len = promises.length
    let count = 0
    return new Promise((resolve, reject) => {
        promises.forEach((promise, index) => {
            Promise.resolve(promise).then(res => {
                count++
                promiseList[index] = res
                if (count === len) {
                    resolve(promiseList)
                }
            }).catch(e => {
                reject(e)
            })
        })
    })
}

const promise1 = Promise.resolve('promise1');
const promise2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 2000, 'promise2');
});
const promise3 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 1000, 'promise3');
});

Promise.all([promise1, promise2, promise3]).then(function(values) {
    console.log(values);
});
  1. 页面上有三个按钮,分别为 A、B、C,点击各个按钮都会发送异步请求且互不影响,每次请求回来的数据都为按钮的名字。 请实现当用户依次点击 A、B、C、A、C、B 的时候,最终获取的数据为 ABCACB。
class Queue {
  promise = Promise.resolve();
  excute(promise) {
    this.promise = this.promise.then(() => promise);
    return this.promise;
  }
}
const queue = new Queue();
const delay = (params) => {
  const time = Math.floor(Math.random() * 5);
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(params);
    }, time * 500);
  });
};
const handleClick = async (name) => {
  const res = await queue.excute(delay(name));
  console.log(res);
};
handleClick('A');
handleClick('B');
handleClick('C');
handleClick('A');
handleClick('C');
handleClick('B');

GET和POST区别

  • GET:一般用于信息获取,使用URL传递参数,对所发送信息的数量也有限制,一般在2000个字符
  • POST:一般用于修改服务器上的资源,对所发送的信息没有限制。
    区别:
  • Get请求可以缓存,POST不能;
  • Post相对Get请求较安全;
  • URL长度限制,会影响Get请求;
  • Post支持更多的编码类型且不对数据类型限制;
  • get参数通过url传递,post放在request body中;

async、await

var a = 0
var b = async () => {
  a = a + await 10
  console.log('2', a) // -> ?
}
b()
a++
console.log('1', a) // -> ?

//1 1  
//2 10

 a = await 10 + a
 //2 11

事件循环

JS是一门单线程、非阻塞的脚本语言,在执行任务时,都只是一个主线程来处理所有的任务,非阻塞就是靠的事件循环(event loop);

  • 组成:主线程、宏队列、微队列(promise.then()、process.nextTick());
  • 执行顺序:主线程->微队列->宏队列->微队列…

JS执行原理:JS是解释执行的,即读取一个语句就执行一个。

JS的定时器是准时的吗?setTimeout(()=>{},0)是立即执行吗?
答:定时器是在事件执行过程中,在本轮任务宏任务、微任务执行之后执行,0秒并非实际意义上的0秒执行。并且如果遇到队列阻塞,时间就会不断延后。写0秒,是因为主线程渲染也会作为一个事件放在消息队列中,如果想要实现在DOM挂载后立即执行,就可以写一个定时器处理。

无论定时器是否已过期,都会一直存在消息队列中,占据内存无法释放。timer也因未知定时器状态而一直不回收。所以用定时器就要处理定时器的销毁清理。

事件流

事件流是网页元素接收事件的顺序,包括三个阶段:事件捕获、处于目标阶段、事件冒泡阶段。父集捕获-子集捕获-子集冒泡-父集冒泡
IE兼容
·attchEvent(‘on’ + type, handler)
·detachEvent(‘on’ + type, handler)

事件冒泡、事件委托、事件捕获

事件冒泡:当父元素添加监听事件,点击子元素后,父元素上的事件会被触发,这就是典型的冒泡;

事件委托:事件委托也叫作事件代理,即利用事件冒泡,只指定一个事件处理程序,就可以管理某一个类型的所有事件,减少dom操作,提高网页性能;
例:ul下的li的点击事件,可以委托给ul实现

事件捕获:与事件委托相反,它是从顶层元素开始,直到事件触发元素。通过DOM2事件模型target.addEventListener(type, listener, useCapture)实现。捕获事件先于委托事件发生,从document开始,也在document结束。

  • 阻止事件的冒泡行为
    event.stopPropagation
    event.cancelBubble=true;//IE浏览器
  • 阻止默认事件
    • return false; // 使用addEventListener时不能用return false
    • event.preventDefault; //IE9以下不兼容
      event.returnValue = false; //兼容IE
//阻止浏览器的默认行为
function stopDefault( e ) {
    //一般情况下
    if ( e && e.preventDefault )
        e.preventDefault();
    //IE中
    else
        window.event.returnValue = false;
    return false;
}

JS 中同步异步输出的顺序

异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)

  • 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack);
  • 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件;
  • 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
  • 主线程不断重复上面的第三步。

注:setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。

Ajax原理和运行机制(异步JavaScript和XML)

  • XMLHttpRequest是Ajax的核心机制,是是一种支持异步请求的技术。也就是JS可以及时向服务器提出请求和处理响应,而不阻塞用户,达到无刷新的效果

    • onreadyStateChange 状态改变所触发事件的时间处理程序
    • responseTextresponseXML 服务器进程返回的数据的字符串形式、DOM兼容的文档数据对象
    • status 状态码
    • status Text 状态码的字符串信息
    • readyState 对象状态值 0 初始化 1 正在加载 2 加载完毕 3 交互 4 完成
  • 原理:通过 xmlHttpRequest 对象来向服务器发出异步请求。xmlHttprequest 可以同步或异步返回 Web 服务器的响应,并且能以文本或一个 DOM 文档形式返回内容

  • 运行机制:

    • 创建XMLHttpRequest对象
    • 通过XMLHttpRequest 发送请求
    • 创建回调函数,监视服务器响应状态,响应完成则执行回调
    • 回调通过DOM动态更新HTML页面
	var xmlHttp;
	if(window.XMLHttpRequest){  //针对除IE6以外的浏览器
	    xmlHttp=new XMLHttpRequest(); //实例化一个XMLHttpRequest
	}else{
	   xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");   //针对IE5,IE6
	}
	xmlhttp.open(method,url,async);
	xmlhttp.send();
	xmlHttp.onreadystatechange()=>{
    if(xmlHttp.readyState === 4 && xmlHttp.status === 200){
		//jsg
    }
  • ajax的请求步骤
    • 创建 XMLHttpRequest对象(异步调用对象)
    • 设置响应HTTP请求状态的函数
    • 打开一个新的HTTP请求,并指定该http请求的方法、URL以及验证信息
    • 发送HTTP请求
    • 在响应回调函数中,根据改变状态和请求状态码获取异步请求返回的数据
    • 渲染返回数据

websocket

长链接,允许服务端主动向客户端推送数据,常用于协同合作、在线教育、社交订阅等。

cookie、webStorage

cookie

在客户访问某个地址时,会将请求交到服务器进行处理,在发送请求的时候,浏览器会将页面的头部信息一并交到服务器进行处理。在处理过程中, cookie会在服务器端生成,在服务器端处理完成后,跟随HTTP响应,在响应头加上 cookie信息,浏览器接到响应,会在客户端建立响应的 cookie,在下次客户进行请求的时候,HTTP请求头便会附带相应的 cookie信息以便Serve追中用户,如果使用cookie保存过多数据会带来性能问题。浏览器将 cookie的信息以name-value的形式存储在本地。 cookie用来保存登录信息,大小限制为4KB左右。

  • expires:cookie的存储时间:浏览器当前会话期间。当用户退出浏览器后销毁。可通过设置expires属性设置。当前使用max-age属性设置,单位为秒。max-age为正数,cookie会被写到对应的cookie文件中,持久化处理;max-age为负数,则被处理为临时cookie,保存在浏览器内存中。关闭窗口或者达到相应的设置时间立即失效。max-age设置为0,击删除cookie。(ie6的cookie在浏览器中,在当前窗口跳转或者新开一个页面,无法共有cookie。其它浏览器能共享
  • domain:domain属性可以使多个web服务器共享cookie。(不能讲一个cookie的域设置成服务器所在的域之外的域);
  • secure:布尔值,指定字网络上如何传输cookie,默认false(即普通的http请求);
  • HttpOnly:限制cookie对HTTP 请求的作用范围。特别的,该属性指示用户代理忽略那些通过“非HTTP”方式对cookie的访问(例如浏览器暴露给js的接口);

使用:document.cookie;以;拼接的值
对cookie的使用基本是设置cookie的name、value以及过期时间(expire)。其中,cookie的name-value中不能包含分号、逗号以及空格符(一般使用编码解决:encodeURIComponentdecodeURIComponent);

浏览器处理:

  • IE6或更低版本最多20个cookie, IE7和之后的版本最后可以有50个cookie, Firefox最多50个cookie,每个cookie长度不能超过4KB,否则会被截掉
  • chrome和Safari没有做硬性限制复制代码
  • IE和Opera 会清理近期最少使用的cookie,Firefox会随机清理cookie

cookie优缺点:

  • 优点:
    • 极高的扩展性和可用性;
    • cookie生命期可控制,使值有效期可控;
  • 缺点:
    • 每个特定域名下最多生成的cookie个数有限制;
    • ie会清理近期最少使用的cookie,Firefox会随机清理;
    • cookie大小有限制,为了兼容性一把不超过4095字节;
    • 安全性问题:cookie被拦截,就会被获取所有的session信息。

localStorage、sessionStorage

localStoragesessionStorage是Html5新增的,仅在浏览器中保存,不与服务器通信;

  • localStorage用于本地数据存储,除非被清除,否则不会过期,一般浏览器大小限制在5MB;
  • sessionStorage(会话级别的存储),接口方法和localStorage类似,这些数据只有在同一个会话中的页面才能访问,并且当会话结束后数据也随之销毁。

cookie和webStorage的区别

Web Storage的概念和cookie相似,区别是它是为了更大容量存储设计的。

  • Cookie的大小是受限的,并且每次请求新页面时都会被发送,浪费带宽;另外cookie还需要指定作用域,不可以跨域调用。
  • webStorage拥有setItemgetItemremoveItemclear等方法,cookie需要前端开发者自己封装setCookiegetCookie
  • cookie的作用是与服务器进行交互,作为HTTP规范的一部分而存在 ,而webStorage仅仅是为了在本地“存储”数据而生

实现浏览器多个标签页通信

调用localstorge、cookies等本地存储方式

跨域

浏览器同源策略:当协议、域名、端口号任意一个不同的时候,都是不同域。不同域之间的请求支援,都是跨域。
同源策略是浏览器最基本的安全功能,会限制cookie等、js和动等无法获取,ajax发送被浏览器拦截。

  • CORS:彻底了解前端跨域CORS
    CORS:跨域资源分享。普通跨域请求:只需要服务器设置Access-Cotroll-Allow-Origin;待cookie跨域请求:前后端共同配置:
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容
 
// 前端设置是否带cookie
xhr.withCredentials = true;

其它:Cookie携带只区分域名,不区分端口;ajax跨域请求下,ajax添加自定义或者原装的请求头,请求会发送两次,第一次预检查请求,第二次正常请求
post(或GET)跨域请求时,分为简单请求和复杂请求,跨域携带自定义或者原装请求头头时是复杂请求。
复杂请求会先发送一个method 为option的请求,目的是试探服务器是否接受发起的请求. 如果服务器说可以,再进行post(或GET)请求。

  • JSONP:利用script标签跨域特性进行请求;
  • 原理:先在全局注册一个回调函数,定义回调数据的处理;与服务端约定好一个同名回调函数名,服务端接收到请求后,将返回一段 Javascript,在这段 Javascript 代码中调用了约定好的回调函数,并且将数据作为参数进行传递。
  • 缺点:它只支持GET请求,而不支持POST请求等其他类型的HTTP请求。
	function JSONP(url, params, callback) {
	    const script = document.createElement("script");
	     // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
	    script.src = url + parseObjToParams({...params, callback: "jsonpCallback"});
	    document.body.appendChild(script);
	    window.jsonpCallback = callback;
	    script.onload = () => {
	        document.body.removeChild(script)
	    }
	}
	
	JSONP("http://www.domain2.com:8080/asd", {name: "vijay"}, (data) => {
	    console.log(data);
	});
	
	//server
	app.use("/asd", (req, res, next) => {
	    res.jsonp({ user: 'tobi' })
	});
  • 服务器代理
    • nginx 反向代理
#proxy服务器
server {
    listen       81;
    server_name  www.domain1.com;
    location / {
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;
        # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
        add_header Access-Control-Allow-Credentials true;
    }
}

缓存

前端HTTP缓存机制

js延迟加载的方式

  • defer
  • async
  • 动态创建DOM方式(创建script,插入到DOM中,加载完毕后callBack)
  • 按需异步载入js

XSS和CSRF区别

  • 跨站脚本攻击(Cross Site Scripting),为了不和层叠样式表 CSS 混淆,故将跨站脚本攻击缩写为 XSS。恶意攻击者往 Web 页面里插入恶意 Script 代码,当用户浏览该页之时,嵌入其中 Web 里面的 Script 代码会被执行,从而达到恶意攻击用户的目的。
  • 跨站请求伪造(Cross-site request forgery),是伪造请求,冒充用户在站内的正常操作。我们知道,绝大多数网站是通过 cookie 等方式辨识用户身份,再予以授权的。所以要伪造用户的正常操作,最好的方法是通过 XSS 或链接欺骗等途径,让用户在本机(即拥有身份 cookie 的浏览器端)发起用户所不知道的请求。

区别:

  • 原理不同,CSRF是利用网站A本身的漏洞,去请求网站A的api;XSS是向目标网站注入JS代码,然后执行JS里的代码。
  • CSRF需要用户先登录目标网站获取cookie,而XSS不需要登录
  • CSRF的目标是用户,XSS的目标是服务器
  • XSS是利用合法用户获取其信息,而CSRF是伪造成合法用户发起请求

HTTP和HTTPS的区别

  • https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用
  • HTTP的URL由http://起始且默认使用端口80,而HTTPS的URL由https://起始且默认使用端口443
  • HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的 SSL 加密传输协议
  • HTTP的连接很简单,是无状态的,HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 http 协议安全

HTTP状态码

  • 1xx表示客户端应该继续发送请求

  • 2xx表示成功的请求

    • 200表示OK,正常返回信息
    • 201表示请求成功且服务器创建了新的资源
    • 202表示服务器已经接受了请求,但还未处理
  • 3xx表示重定向

    • 301表示永久重定向,请求的网页已经永久移动到新位置
    • 302表示临时重定向
    • 304表示自从上一次请求以来,页面的内容没有改变过
  • 4xx表示客户端错误

    • 401表示服务器无法理解请求的格式
    • 402表示请求未授权
    • 403表示禁止访问
    • 404表示请求的资源不存在,一般是路径写错了
  • 5xx表示服务器错误

    • 500表示最常见的服务器错误
    • 503表示服务器暂时无法处理请求

== HTTP option的作用==

  • 获取服务器支持的HTTP方法;
  • 检查服务器性能
    正式跨域之前,浏览器会根据需要发起一次预检(option请求),用来让服务器返回允许的方法(get/post),被跨域访问的 Origin(来源/域),是有是否需要认证信息等。

防抖和节流

函数的节流和防抖都是为了限制函数的执行频次,以优化函数触发频率过高导致的响应速度跟不上触发频率,出现延迟、假死或者卡顿现象。区别在于:假设一个用户一直触发某个函数,且每次触发函数的间隔小于阈值,防抖的情况下只会调用一次,而节流会每隔一定时间调用函数。

  • 防抖(将多次操作合并为一次操作进行):触发高频时间后n秒内函数只会执行一次,如果n秒内高频时间再次被触发,则重新计算时间;
  • 节流(使得一定时间内只触发一次函数):高频时间触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率;

防抖和节流

防抖的实现

  • 对于按钮防点击来说的实现:如果函数是立即执行的,就立即调用,如果函数是延迟执行的,就缓存上下文和参数,放到延迟函数中去执行。一旦我开始一个定时器,只要我定时器还在,你每次点击我都重新计时。一旦你点累了,定时器时间到,定时器重置为 null,就可以再次点击了。
  • 对于延时执行函数来说的实现:清除定时器ID,如果是延迟调用就调用函数
// 这个是用来获取当前时间戳的
function now() {
  return +new Date()
}
/**
 * 防抖函数,返回函数连续调用时,空闲时间必须大于或等于 wait,func 才会执行
 *
 * @param  {function} func        回调函数
 * @param  {number}   wait        表示时间窗口的间隔
 * @param  {boolean}  immediate   设置为ture时,是否立即调用函数
 * @return {function}             返回客户调用函数
 */
function debounce (func, wait = 50, immediate = true) {
  let timer, context, args
  
  // 延迟执行函数
  const later = () => setTimeout(() => {
    // 延迟函数执行完毕,清空缓存的定时器序号
    timer = null
    // 延迟执行的情况下,函数会在延迟函数中执行
    // 使用到之前缓存的参数和上下文
    if (!immediate) {
      func.apply(context, args)
      context = args = null
    }
  }, wait)
  // 这里返回的函数是每次实际调用的函数
  return function(...params) {
    // 如果没有创建延迟执行函数(later),就创建一个
    if (!timer) {
      timer = later()
      // 如果是立即执行,调用函数
      // 否则缓存参数和调用上下文
      if (immediate) {
        func.apply(this, params)
      } else {
        context = this
        args = params
      }
    // 如果已有延迟执行函数(later),调用的时候清除原来的并重新设定一个
    // 这样做延迟函数会重新计时
    } else {
      clearTimeout(timer)
      timer = later()
    }
  }
}

节流的实现
防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。

/**
 * underscore 节流函数,返回函数连续调用时,func 执行频率限定为 次 / wait
 *
 * @param  {function}   func      回调函数
 * @param  {number}     wait      表示时间窗口的间隔
 * @param  {object}     options   如果想忽略开始函数的的调用,传入{leading: false}。
 *                                如果想忽略结尾函数的调用,传入{trailing: false}
 *                                两者不能共存,否则函数不能执行
 * @return {function}             返回客户调用函数   
 */
_.throttle = function(func, wait, options) {
    var context, args, result;
    var timeout = null;
    // 之前的时间戳
    var previous = 0;
    // 如果 options 没传则设为空对象
    if (!options) options = {};
    // 定时器回调函数
    var later = function() {
      // 如果设置了 leading,就将 previous 设为 0
      // 用于下面函数的第一个 if 判断
      previous = options.leading === false ? 0 : _.now();
      // 置空一是为了防止内存泄漏,二是为了下面的定时器判断
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    };
    return function() {
      // 获得当前时间戳
      var now = _.now();
      // 首次进入前者肯定为 true
	  // 如果需要第一次不执行函数
	  // 就将上次时间戳设为当前的
      // 这样在接下来计算 remaining 的值时会大于0
      if (!previous && options.leading === false) previous = now;
      // 计算剩余时间
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;
      // 如果当前调用已经大于上次调用时间 + wait
      // 或者用户手动调了时间
 	  // 如果设置了 trailing,只会进入这个条件
	  // 如果没有设置 leading,那么第一次会进入这个条件
	  // 还有一点,你可能会觉得开启了定时器那么应该不会进入这个 if 条件了
	  // 其实还是会进入的,因为定时器的延时
	  // 并不是准确的时间,很可能你设置了2秒
	  // 但是他需要2.2秒才触发,这时候就会进入这个条件
      if (remaining <= 0 || remaining > wait) {
        // 如果存在定时器就清理掉否则会调用二次回调
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      } else if (!timeout && options.trailing !== false) {
        // 判断是否设置了定时器和 trailing
	    // 没有的话就开启一个定时器
        // 并且不能不能同时设置 leading 和 trailing
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  };

call、apply、bind

  • 作用:动态改变this指向;
  • 相同callbindapply这三个函数的第一个参数都是 this 的指向对象;
  • 区别:接收参数不同
    • call(context,arg1,arg2...):传递给函数的参数必须逐个列举出来
    • apply(context,args):传递给函数的是参数数组
    • bind(context,arguments)除了返回是函数以外,它的参数和call 一样
// 区别:语法不同
//call  接受的是若干个参数列表
fun.call(thisArg[, arg1[, arg2[, ...]]])

//apply  接收的是一个包含多个参数的数组
fun.apply(thisArg, [argsArray])

//bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列
var bindFn = fun.bind(thisArg[, arg1[, arg2[, ...]]])
bindFn()
//相同点:将函数绑定到上下文中,改变`this`指向
//call 
Function.prototype.myCall = function(context, ...args) {
  context = context || window
  let fn = Symbol()
  context[fn] = this
  let result = context[fn](...args)
  delete context[fn]
  return result
}

//apply 
Function.prototype.myApply = function(context) {
  context = context || window
  let fn = Symbol()
  context[fn] = this
  let result
  if (arguments[1]) {
    result = context[fn](...arguments[1])
  } else {
    result = context[fn]()
  }
  delete context[fn]
  return result
}

//bind
Function.prototype.myBind = function (context) {
  var _this = this
  var args = [...arguments].slice(1)
  // 返回一个函数
  return function F() {
    // 因为返回了一个函数,我们可以 new F(),所以需要判断
    if (this instanceof F) {
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}

XMLJSON的区别?

  • JSON相对于XML来讲,数据的体积小,传递的速度更快些。
  • JSONJavaScript的交互更加方便,更容易解析处理,更好的数据交互。
  • JSON对数据的描述性比XML较差。
  • JSON的速度要远远快于XML

性能优化

  • 使用CDN
  • gzip压缩
  • 文本压缩
  • 合并请求
  • 雪碧图
  • 图片懒加载
  • 缓存资源
  • 减少DOM操作

HTML5的文件离线存储怎么使用,工作原理是什么?

  • 在用户没有与因特网连接时,可以正常访问站点或应用,在用户与因特网连接时,更新用户机器上的缓存文件

工作原理:基于一个新建的 .appcache 文件的缓存机制(不是存储技术),通过这个文件上的解析清单离线存储资源,这些资源就会像cookie一样被存储下来。之后在网络处于离线状态时,浏览器会通过被离线存储的数据进行页面展示
使用:

  • index.html中添加 <html manifest="test.manifest">
  • manifest文件的mime-type必须是 text/cache-manifest类型
  • manifest清单格式:
    CACHE MANIFEST#上面一句必须#v1.0.0#需要缓存的文件CACHE:a.jsb.css#不需要缓存的文件NETWORK:*#无法访问页面FALLBACK:404.html
  • 浏览器是怎么对HTML5的离线储存资源进行管理和加载的呢?
  • 在线的情况下,浏览器发现html头部有manifast属性,就会请求manifest文件,如果是第一次访问app,那么浏览器就会根据manifest文件的内容下载相应的资源并且进行离线存储。如果已经访问过app并且资源已经离线存储了,那么浏览器就会使用离线的资源加载页面,然后浏览器会对比新的manifest文件与旧的manifest文件,如果文件没有发生改变,就不做任何操作,如果文件改变了,那么就会重新下载文件中的资源并进行离线存储。离线的情况下,浏览器就直接使用离线存储的资源。

BOM

window对象

BOM对象的核心就是window,表示浏览器的一个实例。window具有双重角色:接口/Global对象。

全局作用域

在全局定义的变量和函数都会成为window对象的属性和方法。区别只是window上定义的可以用delete方法删除但是全局定义的不行,会报错。
尝试访问未声明的变量的时候会抛出错误,但是通过查询window对象,可以避免错误抛出,判断某个未声明的变量是否存在

窗口位置

	//跨浏览器取的窗口左边和上边的位置
	var leftPos = (typeOf window.screenLeft == "number")?window.screenLeft:window.screenX;
	var topPos = (typeOf window.screenTop == "number")?window.screenTop:window.screenY;

导航和打开窗口

使用window.open()可以导航到一个特定的URL,也可以打开一个新的窗口

	window.open('http://baidu.com','_blank','fullscreen=yes,height=780')
	//第四个参数只有在不打开新窗口的时候使用,表示新页面是否取代历史记录中当前页面
	window.open('http://baidu.com','_self','fullscreen=yes,height=780',true)
  1. 弹出窗口
    window.open()会返回一个指向新窗口的引用。新创建的window对象有一个opener属性,保存这打开它的原始窗口对象。将window对象的opener属性置为null,则截断了新创建的标签页与父标签页的通信,并且无法恢复。
	var openWin = window.open('http://baidu.com','_blank');
	openWin.resizeTo(500,500);
	//弹出窗口关闭后,窗口的引用仍然存在,用处:检测closed属性
	openWin.close();
	console.log(openWin.closed)//true
  1. 安全限制
    不同浏览器处理机制不同

  2. 弹出窗口屏蔽程序
    检测弹出窗口是否被屏蔽(但无法检测屏蔽原因)

	var blocked = false;
	try{
		var openWin = window.open('http://baidu.com','_blank');
		//内置屏蔽程序阻止窗口弹出,会返回null
		if(openWin == null){
			blocked = true;
		}
	}catch(ex){
		//扩展其它程序阻止窗口弹出,会抛出错误
		blocked = true;
	}
	if(blocked){
		alert('the popup was blocked!')
	}

间歇调用和超时调用

	//超时调用
	var timeoutId = setTimeout(function(){
		console.log('超时调用')
	}1000);
	//取消超时调用
	clearTimeout(timeoutId)
	
	//间歇调用
	var intervalId = setInterval(function(){
		console.log('间歇调用')
	}1000);
	//取消间歇调用
	clearTimeout(intervalId )

location对象

location对象是最有用的BOM对象之一。它提供了与当前窗口中加载的文档有关的信息和一些导航功能。
window.location == document.location

  • location.hash
  • location.host
  • location.hostname
  • location.href
  • location.pathname
  • location.port
  • location.protocol
  • location.search

查询字符串参数

	function getQueryStringArgs(){
		//去掉查询字符串的?
		var qs = (location.search.length > 0 ? location.search.substring(1) : "" );
		args = {};
		items = qs.length ? qs.split("&") : [];
		item = null,name = null, val = null;
		for(var i = 0;i < items.length;i++){
			item = items[i].split("=");
			name = decodeURIComponent(item[0]);
			val = decodeURIComponent(item[1]);
			if(name.length){
				args[name] = value;	
			}
		}
		return args;
	}

位置操作

  • location.assign(url) 立即打开新的URL并且在浏览器的历史记录中生成一条历史记录,等同location.href = url
  • location.hash(search/hostname/pathname/port) = ... 每次修改location的属性(hash除外),页面都会以新的URL重新加载,生成新的历史记录
  • location.replace(url) 替换当前URL,不能后退
  • location.reload(boolean) 重新加载当前页面,不传递任何参数的时候以有效方式加载,传递参数true表示强制从服务器重新加载

navigator对象

  • navigator.userAgent
  • navigator.plugins

history对象

  1. history.go() -1 : 后退 1: 前进 2:前进2页
    也可以传递字符串,浏览器会跳转到历史记录中包含该字符串的第一个位置

  2. hostory.back()

  3. hostory.forward()

刷新和强制刷新有什么区别?说说你对两者的理解

从输入URL到显示页面,都经历了什么?

  1. 浏览器查看缓存,有缓存则直接显示;
  2. 发送http请求前,进行域名解析,获取相应的IP地址;
  3. 浏览器想服务器发起tcp连接,三次握手;
  4. 握手成功,发起http请求,请求数据包;
  5. 服务器处理,将数据返回浏览器;浏览器接受http响应,读取页面内容,浏览器渲染;
  6. 客户端服务器交互,ajax查询;

DOM

节点层次

Node类型

  1. 节点关系
    文档树可比做家谱。
    每个节点都有一个childNodes属性,保存着一个NodeList对象。NodeList对象是一个类数组对象【不是Array的实例】,用于保存一组有序的节点,可通过位置来访问。

    childNodes可以通过[]访问,也可以使用item访问

	var firstChild = someNode.childNodes[0];
	var secondChild = someNode.childNodes.item(1);
	var len = someNode.childNodes.length;

将类数组对象转换为数组的方法
Array.prototype.slice.call(someNode.childNodes,0)(不兼容ie9以下)

  • parentNode属性,指向文档树中的父节点。
  • previousSiblingnextSibling属性,指向同胞相邻节点
  • firstChildlastChild属性
  • hasChildNodes() 判断节点是否有子节点
  1. 操作节点
    • appendChild()insertBefore(newNode,target) 返回被插入节点
    • replaceChild(new,old)removeChild 返回被移除的节点(被替换/删除的节点仍然存在于文档中,只是没有位置)
  2. 其它关系
  • cloneNode(true) 创建一个node的副本。该方法接收一个参数,表示是否执行深复制(复制节点及其节点数)。
    节点克隆不会复制事件处理程序等,但是ie会。所以建议在复制节点前先移除事件处理程序
  • normalize() 处理文档中的文本节点(节点调用该方法,则会查找该节点的后代节点中是否存在空文本节点,有则删除;相邻文本节点则合并为一个文本节点)

Document类型

在浏览器中,document对象是HTMLDocument的一个实例,表示整个HTML页面。

  1. 文档的子节点

    • document.documentElement属性,始终指向<html>元素;
    • document.body 属性,始终指向<body>元素
  2. 文档信息

    • document.title 文档标题
    • document.URLdocument.domaindocument.referrer 只有document.domain可修改
  3. 查找元素

    • document.getElemntById(id) id大小写匹配!不存在则返回null
    • document.getElemntsByTagName(tagName) 返回HTMLCollection。该对象和NodeList一样,还有一个专有方法nameItem(name)可获取相关name属性的集合
    • document.getElementsByName(name)
  4. 文档写入

    • document.write()
    • docuemnt.wirteIn() 会在末尾添加一个换行符\n

Element类型

要访问元素的标签名,可以使用nodeNametagName两个属性

  1. HTML 元素
    idtitle
  2. 取的特性
    • getAttribute()
    • setAttribute()
    • removeAttribute()
  3. 设置特性
    没有属性时则自动添加
  4. attrribute属性
    attribute属性中包含一系列节点,每个节点的nodeName就是特性的名称,nodeValue是特性的值
  5. 创建元素
    docuemnt.createElement(tagName),创建元素的同时也为新元素设置了 ownerDocument属性,可操作元素属性
    • 在ie中可以直接传入完整的元素标签,还可以包含属性(有助于避开ie7之前的动态创建元素的一些问题)
  6. 元素的子节点

Text类型

  1. 创建文本节点
    docuemnt.createTextNode('....'),创建元素的同时也为新元素设置了 ownerDocument属性,可操作元素属性
  2. 规范化文本节点
    element.normalize()
  3. 分割文本节点
    element.splitText()

Comment类型

iframe的使用场景有哪些?

1:典型系统结构,左侧是功能树,右侧就是一些常见的table或者表单之类的。为了每一个功能,单独分离出来,公共文件抽离采用iframe。
2:ajax上传文件。
3:加载别的网站内容,例如google广告,网站流量分析。
4: 在上传图片时,不用flash实现无刷新。
5: 跨域访问的时候可以用到iframe,使用iframe请求不同域名下的资源。
6:下载文件,使用window.open打开选项卡的话,页面会有闪动,用iframe来下载文件,可避免上面的问题

DOM扩展

选择符API

querySelector()

document.querySelector(css选择符) 返回与该模式匹配的第一个元素,没有则返回null。
element.querySelector(css选择符) 返回与该模式匹配的第一个元素,没有则返回null。

querySeletorAll()

document.querySeletorAll(css选择符) 返回与该模式匹配的NodeList实例

元素大小

  1. 偏移量:元素在屏幕上占用的所有可用空间
  • offsetHeight 垂直方向上所占空间height+scrollHeight+2*border
  • offsetWidth 水平方向上所占空间
  • offsetTop 元素上边框至包含元素的上内边框的高度
  • offsetLeft
    包含元素的引用包含在offsetParent属性中。要取的一个元素的偏移量:div.offsetTop + div.offsetParent
  1. 客户区大小:元素内容与其内边距所占的空间大小
  • clientWidth
  • clientHeight
  1. 滚动大小:包含滚动内容的代销
  • scrollHeight 在没有滚动条的时候,元素内容的高度
  • scrollWidth
  • scrollTop 被隐藏在元素上方的像素数。可通过改属性改变元素的滚动位置
  • scrollLeft
  1. 确定元素大小
    getBoundingClientRect(left,top,right,bottom)

滚动

scrollIntoView() :通过滚动浏览器窗口或者某个容器元素,使调用元素出现在视口当中。
传值:true时或者不传值,窗口滚动之后会让调用元素的顶部与视口顶部平齐;false时,与底部平齐。

  1. scrollIntoViewIfNeed(aligncenter) 只有在当前窗口不可见时才滚动浏览器窗口或者容器至可见处
  2. scrollByLines(lineContent) 将元素的内容滚动至指定的行高
  3. scrollByPages(pageContent) 将原始的内容滚动到指定的页面高度。

返回顶部的方法有哪些?

  • window.scrollTo(0,0); //ie不支持,但好用
  • document.documentElement.scrollTop = 0;
  • location.href += ‘#’;

在浏览器中输入url到页面显示出来的过程发生了什么?

https://blog.yyge.top/blog
https://dailc.github.io/2018/03/12/whenyouenteraurl.html

从一行代码里学点js

https://my.oschina.net/l3ve/blog/330358

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值