前端基础知识
数据类型
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有两种属性:
数据属性
和访问器属性
- 数据属性(包含一个数据值的位置):
[[Configurable]]
:是否可修改属性特性、删除属性、把属性修改为访问器属性(默认为true
)[[Enumerable]]
:是否可以通过for-in循环返回属性(默认为true
)[[Writable]]
:是否可以修改属性值(默认为true
)[[Value]]
:包含这个属性的数据值(默认为undefined
)
修改属性特性,需要使用ECMAScript5的
Object.definedProperty(属性所在对象,属性名,描述符对象)
方法,其中描述符必须是数据属性之一。设置一个或者多个值,可以修改对应的特性值
把[[Configurable]]
设置为false后,则调用Object.definedProperty()
方法也无法修改
- 访问器属性:
[[Configurable]]
:是否可修改属性特性、删除属性、把属性修改为访问器属性(默认为true
)[[Enumerable]]
:是否可以通过for-in循环返回属性(默认为true
)[[Get]]
:在读取属性时调用的函数(默认为undefined
)[[Set]]
:在写入属性时调用的函数(默认为undefined
)
访问器属性不能直接定义,必须使用Object.definedProperty()
定义
读取属性的特性(ECMAScript5)
Object.getOwnPropertyDescriptor(属性所在对象,描述符名称)
可以取得给定属性的描述符
String类型
- 字符串方法:
str.charAt(index)
返回给定位置的字符str.charCodeAt(index)
返回给定位置的字符的字符编码ES5
中可以使用str[index]
访问指定位置的字符
- 字符串操作方法
concat()
字符串拼接substr(start,num)
返回从起始位置截取num个字符(参数为负值时,-start+字符串长度,-num=0)substring(start,end)
返回从起始位置到结束位置的字符(参数为负值时,全部转为0)slice(start,end)
返回从起始位置到结束位置的字符(参数为负值时,负值+字符串长度进行转换)
- 字符串位置方法
indexOf(str,start)
和lastIIndexOf(str,start)
返回字符在字符串中的位置
- trim()方法(
ES5
新提供)—— 删除字符串首尾空格 - 字符串大小写转换
toLowerCase
toUpperCase
- 字符串的模式匹配方法
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
null
和undefined
的区别
-
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
- == 和===的区别
- ==, 两边值类型不同的时候,要先进行类型转换,再比较
- ===,不做类型转换,类型不同的一定不等;
- 特殊数据类型解析
- 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
的指向,只会传入参数; - 箭头函数没有原型属性;
- 箭头函数返回对象时,要加一个小括号
函数
函数是对象,函数名是指向函数对象的指针。一个函数可以有多个函数名。
函数定义方式:
- 函数声明 (存在
函数声明提升
)- 函数表达式 变量 = 匿名函数
参数
arguments
类数组对象,主要用于保存参数
arguments
的属性callee
:指针,指向拥有这个arguments对象的函数,递归调用的时候使用arguments.callee()
,可以避免因为函数名重复时造成的bugthis
引用的是函数执行的环境对象(全局作用域调用函数时,this引用的是window对象)caller
:ES5
规范的函数对象属性,保存调用当前函数的函数的引用,可可以使用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
属性和方法
- 每个函数都包含两个非继承而来的方法:
apply()
和call()
,两者作用相同,仅仅是接收参数的格式不同
fun.apply(obj,[...arguments])
fun.call(obj,...arguments)
作用:
- 用于在特定的作用域中调用函数,即重新定义函数体内this对象的值
- 扩充函数作用域,对象不需要与方法有任何的耦合关系,函数中操作使用this指向
ES5
中新定义方法:bind()
创建一个函数的实例,其this值会绑定到传给bind()函数的值
函数重载
函数没有签名(接收的参数类型和数量),所以ECMAScript没有函数重载。定义相同名称的函数,只会执行最后一个。
递归
定义:一个函数通过名字调用自身
arguments.callee()
可以解决函数重定义情况
arguments.callee()
是一个指向正在执行的函数的指针,可用来代替函数名,因此可以用来实现递归调用- 严格模式下
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
的第一个参数使用字符串而非函数的话,会引发内存泄漏。- 闭包、控制台日志、死循环(在两个对象彼此引用且彼此保留时,就会产生一个循环
- 过度递归
- 无效的全局变量
闭包
- 定义:假如一个函数能访问外部的变量,那么这个函数就是一个闭包。闭包常用来访问私有变量。
this
和argument
对象:与外层函数无关,只与调用自己时的对象以及传的参数有关。- 特性:
- 函数嵌套函数
- 函数内部可以引用外部的参数和变量
- 参数和变量不会被垃圾回收机制回收
for (var i = 0; i < 6; i++) {
setTimeout(() => {
console.log(i)
})
} //输出6 解决办法:let定义i
-
数据存放在堆中还是栈中?(栈是有结构的,先进后出,数据查询速度:栈>堆)
JS中基本类型值在内存中固定大小,保存在栈内存中;引用类型是对象,保存在堆内存中;JS在定义变量时就分配了内存,使用时候就是对内存的读写操作,内存释放依赖于浏览器的垃圾回收机制; -
垃圾回收
JS每次创建字符串、对象、数组等,解释器都必须动态分配内存来存储实体。这种动态分配了内存的,最终都要释放这些内存一遍他们能够再利用,否则系统内可用内存会被消耗完,造成系统崩溃。
常用回收方法
- 标记清除;
- 引用计数
- 闭包的优缺点
- 优点
- 读取私有变量;
- 可封装对象的私有属性和私有方法;让这些变量时装保存在内存中;
- 缺点
- 占用更多内存;滥用闭包可能会造成网页性能问题,在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 这些一致
- 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"
- 组合继承
- 原型式继承
- 寄生式继承
- 寄生组合市继承
- 通过原型实现的继承和class有什么区别?
类从类继承并创建子类关系;原型是一个工作对象实例。对象直接从其他对象继承。
单体内置对象
Global
对象
所有在全局作用域中定义的属性和函数,都是Global对象的属性和方法
- URI 编码方法
encodeURI()
、encodeURIComponent()
、decodeURI()
、decodeURIComponent()
evval(str)
方法
eval() 类似一个JavaScript解析器,只接收一个参数(要执行的javascript字符串)
- 在
eval()
中定义的变量和函数都不会被提升;只在eval()执行的时候创建 - 严格模式下,在外部无法访问
eval()
中定义的任何变量或者函数 - 使用eval(),存在代码注入的恶意攻击危险
window
对象
在全局作用域中声明的变量和函数,都成为了window对象的属性
Math
对象
min()
和max()
方法
可接收任意多个数值参数。求数组中的最大值方法
//将Math对象作为apply的第一个参数,从而正确设置this的值
var maxValue = Math.max.apply(Math,arr)
- 舍入方法
Math.ceil()
向上舍入Math.floor()
向下舍入Math.round()
标准舍入
Math.random()
方法
用于返回一个0~1之间的随机数- 其它 方法
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){ }
- 所有对象属性都被返回一次,但是先后次序可能会因为浏览器不同而有差异
- 要迭代的对象的变量值为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
break
和continue
语句
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=>{
//执行成功操作
})
- 使用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]
});
- 手写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);
});
- 页面上有三个按钮,分别为 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
状态改变所触发事件的时间处理程序responseText
、responseXML
服务器进程返回的数据的字符串形式、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中不能包含分号、逗号以及空格符(一般使用编码解决:encodeURIComponent
、decodeURIComponent
);
浏览器处理:
- 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
localStorage
、sessionStorage
是Html5新增的,仅在浏览器中保存,不与服务器通信;
localStorage
用于本地数据存储,除非被清除,否则不会过期,一般浏览器大小限制在5MB;sessionStorage
(会话级别的存储),接口方法和localStorage
类似,这些数据只有在同一个会话中的页面才能访问,并且当会话结束后数据也随之销毁。
cookie和webStorage的区别
Web Storage的概念和cookie相似,区别是它是为了更大容量存储设计的。
Cookie
的大小是受限的,并且每次请求新页面时都会被发送,浪费带宽;另外cookie还需要指定作用域,不可以跨域调用。webStorage
拥有setItem
、getItem
、removeItem
、clear
等方法,cookie
需要前端开发者自己封装setCookie
、getCookie
。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;
}
}
缓存
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
指向; - 相同:
call
、bind
、apply
这三个函数的第一个参数都是 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))
}
}
XML
和JSON
的区别?
JSON
相对于XML
来讲,数据的体积小,传递的速度更快些。JSON
与JavaScript
的交互更加方便,更容易解析处理,更好的数据交互。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)
- 弹出窗口
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
-
安全限制
不同浏览器处理机制不同 -
弹出窗口屏蔽程序
检测弹出窗口是否被屏蔽(但无法检测屏蔽原因)
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
对象
-
history.go()
-1 : 后退 1: 前进 2:前进2页
也可以传递字符串,浏览器会跳转到历史记录中包含该字符串的第一个位置 -
hostory.back()
-
hostory.forward()
刷新和强制刷新有什么区别?说说你对两者的理解
- 刷新: 若页面之前访问过,可以读取缓存的内容
- 强制刷新: 当前页面不读取缓存的内容,所有的内容必须从服务器获取
性能优化——浏览器的缓存策略
从输入URL到显示页面,都经历了什么?
- 浏览器查看缓存,有缓存则直接显示;
- 发送http请求前,进行域名解析,获取相应的IP地址;
- 浏览器想服务器发起tcp连接,三次握手;
- 握手成功,发起http请求,请求数据包;
- 服务器处理,将数据返回浏览器;浏览器接受http响应,读取页面内容,浏览器渲染;
- 客户端服务器交互,ajax查询;
DOM
节点层次
Node
类型
-
节点关系
文档树可比做家谱。
每个节点都有一个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
属性,指向文档树中的父节点。previousSibling
和nextSibling
属性,指向同胞相邻节点firstChild
和lastChild
属性hasChildNodes()
判断节点是否有子节点
- 操作节点
appendChild()
和insertBefore(newNode,target)
返回被插入节点replaceChild(new,old)
和removeChild
返回被移除的节点(被替换/删除的节点仍然存在于文档中,只是没有位置)
- 其它关系
cloneNode(true)
创建一个node的副本。该方法接收一个参数,表示是否执行深复制(复制节点及其节点数)。
节点克隆不会复制事件处理程序等,但是ie会。所以建议在复制节点前先移除事件处理程序normalize()
处理文档中的文本节点(节点调用该方法,则会查找该节点的后代节点中是否存在空文本节点,有则删除;相邻文本节点则合并为一个文本节点)
Document
类型
在浏览器中,document
对象是HTMLDocument
的一个实例,表示整个HTML页面。
-
文档的子节点
document.documentElement
属性,始终指向<html>元素;document.body
属性,始终指向<body>元素
-
文档信息
document.title
文档标题document.URL
、document.domain
、document.referrer
只有document.domain
可修改
-
查找元素
document.getElemntById(id)
id大小写匹配!不存在则返回nulldocument.getElemntsByTagName(tagName)
返回HTMLCollection。该对象和NodeList一样,还有一个专有方法nameItem(name)
可获取相关name属性的集合document.getElementsByName(name)
-
文档写入
document.write()
docuemnt.wirteIn()
会在末尾添加一个换行符\n
Element
类型
要访问元素的标签名,可以使用nodeName
和tagName
两个属性
- HTML 元素
id
、title
… - 取的特性
getAttribute()
setAttribute()
removeAttribute()
- 设置特性
没有属性时则自动添加 attrribute
属性
attribute
属性中包含一系列节点,每个节点的nodeName就是特性的名称,nodeValue是特性的值- 创建元素
docuemnt.createElement(tagName)
,创建元素的同时也为新元素设置了ownerDocument
属性,可操作元素属性- 在ie中可以直接传入完整的元素标签,还可以包含属性(有助于避开ie7之前的动态创建元素的一些问题)
- 元素的子节点
Text
类型
- 创建文本节点
docuemnt.createTextNode('....')
,创建元素的同时也为新元素设置了ownerDocument
属性,可操作元素属性 - 规范化文本节点
element.normalize()
- 分割文本节点
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实例
元素大小
- 偏移量:元素在屏幕上占用的所有可用空间
offsetHeight
垂直方向上所占空间height+scrollHeight+2*borderoffsetWidth
水平方向上所占空间offsetTop
元素上边框至包含元素的上内边框的高度offsetLeft
包含元素的引用包含在offsetParent
属性中。要取的一个元素的偏移量:div.offsetTop + div.offsetParent
- 客户区大小:元素内容与其内边距所占的空间大小
clientWidth
clientHeight
- 滚动大小:包含滚动内容的代销
scrollHeight
在没有滚动条的时候,元素内容的高度scrollWidth
scrollTop
被隐藏在元素上方的像素数。可通过改属性改变元素的滚动位置scrollLeft
- 确定元素大小
getBoundingClientRect(left,top,right,bottom)
…
滚动
scrollIntoView()
:通过滚动浏览器窗口或者某个容器元素,使调用元素出现在视口当中。
传值:true时或者不传值,窗口滚动之后会让调用元素的顶部与视口顶部平齐;false时,与底部平齐。
scrollIntoViewIfNeed(aligncenter)
只有在当前窗口不可见时才滚动浏览器窗口或者容器至可见处scrollByLines(lineContent)
将元素的内容滚动至指定的行高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