一、预编译、作用域、闭包
1、js执行三步曲
- 语法分析:检查通篇语法错误
- 预编译: 函数整体提升,变量的声明提升
- 解释执行:解释一行执行一行
2、全局中的预编译(有暗示全局对象的作用)
- 1.创建GO对象(全局上下文)(GO===window)
- 2.找出变量声明和函数声明放到GO{}里面,且值为undefined
- 3.执行(赋值)
3、函数中的预编译
- 1.创建AO对象(AO执行期上下文)
- 2.找出形参、变量声明、函数声明放到AO{}里面,且值为undefined
- 3.执行(实参和形参相统一、变量赋值、找出函数里面的函数声明并且函数体为值)
4、作用域
- 1.作用域是代码定义的时候决定的
- 2.用来决定代码执行的范围,变量所属的范围
- 3.作用:
- 隔离变量
- 规定其之后的作用域链是什么:[[scopes]]:上一级作用域链
5、作用域链
- 在js代码正式执行之前创建的
- 作用域链就是变量查找的范围
- 在当前作用域引用变量时,如果没有此变量,则会一路往父级作用域查找此变量,直到全局作用域,如果都没有,在非严格情况下会自动声明,所以是undefined,在严格条件下则会报错
- 作用域链是一个数组结构
6、执行上下文
- js引擎在js代码执行之前先创建一个执行环境
- 进入该环境创建一个变量对象,该对象用于收集当前环境下的:变量、函数、函数参数、this
- 确认this指向
- 创建作用域链
7、执行上下文栈
- 用于保存执行上下文
- 先进后出
- 执行上下文是动态创建的:函数每调用一次就创建一次执行上下文,执行完就销魂
8、闭包
定义
- 闭包是一个存在内部函数的引用关系
- 该引用指向的是外部函数的局部变量对象(即内部函数使用了外部函数的局部变量)
形成条件
- 函数嵌套
- 内部函数引用外部函数的局部变量
- 内部函数被使用,注意: 函数变量提升的时候,如果内部函数没有被使用,在预解析的过程中不会定义内部函数
优缺点
- 延长外部函数变量对象的生命周期
- 使用闭包能够间接的从函数外部访问函数内部的私有变量
二、函数、this
1、this
this指向
call、apply、bind
call/apply 改变this指向 并且立即执行 call(context,原函数的参数们依次排列) apply(context,原函数的参数集合 用数组装载) bind 改变this指向 并返回一个新函数 写法同call
2、立即执行函数
-
执行完,就销毁
-
只有函数表达式才能被立即执行符号执行
三、数组和对象
1、数组
数组转换
- join()方法接收一个字符串作为分隔符,并返回用分隔符连接的数组项字符串
- toString()方法返回数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串
栈方法
- push()方法接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后的数组长度
- pop()方法从数组末尾移除最后一项,减少数组的length,返回移除的项
队列方法
- shift()方法移除数组中的第一项,并返回该项,同时数组长度减1
- unshit()方法在数组前端添加任意个项,并返回新数组的长度
排序方法
- reverse()方法用于反转数组中每一项,并返回反转后的数组
- sort()方法用将数组排序,并返回排序后的数组
- 若不传compareFunction,sort()方法回调用每个数组项的toString()方法,然后比较得到的字符串
- 若传compareFunction(a,b),如果返回值小于0,则a位于b之前,如果返回值等于0则位置不变,如果返回值大于,则b位于a之前
操作方法
- concat()方法创建当前数组一个副本,然后将接收到的参数添加到这个副本末尾,最后返回新构建的数组
- slice()方法截取当前数组中的一或多个项创建一个新数组
- splice()
- 删除:可以删除任意数量的项,指定两个参数:删除的第一项位置和删除的数量
- 插入:可以向指定位置插入任意数量的项,第一个参数:插入的位置,第二个参数0(删除0),第三个参数以后要插入的项
- 替换:可以将指定位置的项替换,第一个参数要替换项的位置,第二个替换项个数,第三个参数以后新的项
位置方法
- indexOf()方法从头开始查找指定项,找到返回对应数组下标,没找到返回-1
- lastIndexOf()方法用法和indexOf基本一致,只是从数组尾部开始查找
迭代方法
- every()方法对数组中每一项运行给定函数,如果该函数对每一项都返回true,则返回true
- some()方法对数组中任意一项运行给定函数,如果该函数对任意一项返回true,则返回
- map()方法对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组
- filter()方法对数组中的每一项运行给定函数,返回该函数调用会返回true的项组成的数组
- forEach()方法对数组中的每一项都运行给定函数,没有返回值
缩小方法
-
reduce()方法对数组中的每一项执行一个reducer函数(升序执行),并将结果汇总为单个返回值
- 参数:(callback(accumulator(累计器累计回调的返回值),currentValue(数组中正在处理的元素),currentIndex(数组中正在处理的元素的索引,如果提供了initialValue,则起始索引号为0,否则为1,可选),array(调用reducer的数组))=>{}, initialValue(作为第一次调用callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错,可选))
-
reduceRight()方法用法与reduce()方法一致,只是redeceRight()方法调用从数组尾部开始,向前遍历
ES6新增
- from()方法将类似数组的对象和可遍历的对象转化为数组
- 参数:arrayLike(想要转换成数组的伪数组对象或可迭代对象),mapFn(如果指定了该参数,新组数中的每个元素会执行此回调函数,可选),thisArg(执行回调函数时this对象,可选)
- of()方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型
- 参数:item...(任意个参数,将按顺序返回数组中的元素)
- copyWithin()方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有位置成员),返回当前数组
- 参数:target(从该位置开始替换数据),start(从该位置开始读取数据,默认为0,如果为负值表示倒数,可选),end(到该位置前,停止读取数据,默认为数组长度,如果为负值,表示倒数)
- find()方法用于找出第一个符合条件的数组成员,若没有符合条件的,返回undefined
- 参数:callback(item, index, arr)
- findIndex()方法用法与find()方法相似,返回第一个符合条件的成员的位置,若没有符合条件的,返回-1
- fill()方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素,不包括终止索引
- 参数:value(填充数组元素的值),start(起始索引,可选),end(终止索引,可选)
- entries()、keys()、values() , 三个数组遍历方法,返回一个遍历器对象,entries()键值对的遍历,keys()键名的遍历,values()键值的遍历
- 参数:无
- includes()方法用来判断一个数组是否包含一个指定的值,如果包含返回true,否则返回false
- 参数:value(要查找的元素),start(开始查找的位置,可选)
- flat()方法会按照一个指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素和并到一个新数组中返回
- 参数:depth(要提取数组的嵌套结构深度,默认为1,可选)
总结
不改变原数组
- es3
- concat 返回 拼接后的数组
- join 返回 在数组元素中插入指定元素后的字符串
- slice 返回 截取后的数组
- toString 返回 数组的字符串
- es5
- every 返回 布尔值
- some 返回 布尔值
- filter 返回 数组元素符合过滤条件之后的数组
- reduce
- 例如
var arr = [1,2,3],res=[]; arr.reduce(function(prev,vlaue){ prev.push(value + 1); return prev; },res) - reduceRight
改变原数组
- es3
- pop 返回 删除的数组最后一个元素
- push 返回 长度
- shift 返回 删除的数组第一个元素
- unshift 返回 长度
- reverse 返回 倒序的数组
- sort 返回 正序的数组
- splice 返回 新数组
不确定改不改变
- es5
- forEach 没有返回值
- 例如
//操作带有数组下标时 var arr = [1,2,3]; arr.forEach(function(value,index){ arr[index] += 1 }) - map 返回 新数组
- 例如
//操作了引用关系,会改变原数组 var arr = [{value:1},{value:2},{value:3}],res = []; res = arr.map(function(value,index){ value.name = index; return value; } console.log(arr,res)
- forEach 没有返回值
数组去重
1、for循环
- 循环两个数组,两个数组相比较,不重复就push
function uniqueArr(array) { var _arr = [], isRepeat; for (let i = 0; i < array.length; i++) { isRepeat = false; for (let j = 0; j < _arr.length; j++) { if(_arr[j] == array[i]){ isRepeat = true; break; } } if(!isRepeat){ _arr.push(array[i]) } } return _arr } - 循环两次数组,项和后边的项相比较,存在重复的的break,不存在重复的push
function uniqueArr(array) { var _arr = [], isRepeat; for (let i = 0; i < array.length; i++) { isRepeat = false; for (let j = i+1; j < array.length; j++) { if(_arr[j] == array[i]){ isRepeat = true; break; } } if(!isRepeat){ _arr.push(array[i]) } } return _arr }
filter+indexOf
function uniqueArr(array) {
return array.filter(function (item,index) {
//这个元素的第一次出现的下标==当前filter循环到的下标,则返回,说明是不重复的;如果不相等,说明已经出现过,是重复的
return array.indexOf(item) === index;
})
}
forEach+indexOf
function uniqueArr(array) {
var _arr = [];
array.forEach(function (item) {
if(_arr.indexOf(item) === -1){
_arr.push(item)
}
})
return _arr
}
sort
function uniqueArr(array) {
var _arr = [];
//先简单排序
arr.sort();
for (let i = 0; i < array.length; i++) {
//arr当前元素和后一个元素不同,push
//if(array[i] !== array[i+1]){
//或,arr当前元素和新数组_arr不同,push
if(array[i] !== _arr[_arr.length-1])
_arr.push(array[i])
}
}
return _arr
}
ES6 includes+forEach
function uniqueArr(array) {
var _arr = [];
array.forEach(function (item) {
if (!_arr.includes(item)) {
_arr.push(item)
}
})
return _arr
}
indexOf和includes区别
indexOf: -1 index 具体位置,对NaN无效
includes: true false,对NaN有效
reduce+sort
function uniqueArr(array) {
return array.sort().reduce(function (prev, item) {
//先把arr简单排序,如果prev指向的数组为空,或者,prev指向的数组的最后一项和当前的item不想等,push
if (prev.length === 0 || prev[prev.length - 1] !== item) {
prev.push(item)
}
return prev
}, []);
}
Map
//Map的键名可以是一个对象
function uniqueArr(array) {
var _arr = [],
_temp = new Map();
for (let i = 0; i < array.length; i++) {
if(!_temp.get(array[i])){
_temp.set(array[i],1);
_arr.push(array[i])
}
}
return _arr
}
//相似地,
function uniqueArr(array) {
var _arr = [],
_temp = new Object();
for (let i = 0; i < array.length; i++) {
if(!_temp[array[i]]){
_temp[array[i]] = 1;
_arr.push(array[i])
}
}
return _arr
}
Set
//new Set(),不是一个数组,而是一个对象,用Array.from转换
function uniqueArr(array) {
return Array.from(new Set(array))
}
数组扁平化,去重,排序
- 1.扁平化
- 法一:
function flatten(arr) { var _arr = arr || [], fArr = [], len = _arr.length, item; for (let i = 0; i < len; i++) { item = arr[i]; _isArr(item)?fArr = fArr.concat(flatten(item)):fArr.push(item); } return fArr; function _isArr(item) { return {}.toString.call(item) === '[object Array]'; } }- 法二:
Array.prototype.flatten = function () { var _arr = this, toStr = {}.toString; if(toStr.call(_arr)!=='[object Array]'){ throw new Error('只有数组才能调用flatten方法') } //用forEach //var fArr = []; //_arr.forEach(function (item) { // toStr.call(item) === '[object Array]'? fArr = //fArr.concat(item.flatten()): fArr.push(item); //}) //用reduce return _arr.reduce(function (prev,item){ return prev.concat( toStr.call(item) === '[object Array]'?item.flatten():item ); },[]); }- 法三:
const flatten = arr => arr.reduce((prev, item) => prev.concat( {}.toString.call(item) === '[object Array]' ? flatten(item) : item),[])- 法四:
arr.flat(Infinity) - 2.去重
Array.from(new Set(arr.flat(Infinity)))
- 3.排序
Array.from(new Set(arr.flat(Infinity))).sort((a,b)=>a-b)
原型、继承
什么是原型对象
- 每个函数都有一个prototype属性,该属性指向的是原型对象(显示原型对象)
- 每个实例对象都有一个
__proto__属性,该属性指向的是也是原型对象(隐式原型对象) - 构造函数的显示原型 === 当前构造函数实例对象的隐式原型对象
- 原型对象的本质:普通的Object实例
什么是原型链
- 查找对象的属性,现在自身找,如果自身没有,就沿着
__proto__找原型对象 - 如果原型对象还没有,继续沿着
__proto__,直到找到Object的原型对象 - 如果还没有返回undefined
- 原型链:沿着
__proto__查找的这条链就是原型链
A instanceof B
- 判断A对象的原型链上有没有B函数的显示原型对象,返回布尔值
new 创建实例的过程
- 创建一个空对象
{} - 该空对象的原型指向构造函数(链接原型)
son.__proto__ = Father.prototype - 绑定 this:将对象作为构造函数的 this 传进去,并执行该构造函数;
Father.call(son) - 为新对象属性赋值
- 返回新对象
return this,此时的新对象就拥有了构造函数的方法和属性了
继承
原型链继承:子类的原型 成为 父类的实例
Child.prototype = new Person();
构造器丢了,手动添加回来
Child.prototype.constructor = Child;
借用构造函数继承(不是真继承)
在子类构造函数中调用父类构造函数
Person.call(this,name,age)
组合继承
原型继承+借用构造函数继承