1.全局作用域:
js中所有声明的变量都存在window对象中。var声明的变量会提升,function的两种声明,第一种function test(){}, 函数会提前创建,所以在任何地方地方调用都不会报错;第二种函数声明var test = function(){};函数不会提前创建,如果在函数声明之前调用函数会报错
2.函数作用域:
调用函数时创建函数作用域,函数执行完毕后,销毁函数作用域;每调用一次函数,就会创建一个函数作用域,它们是互相独立的
3.使用工厂方法创建对象:
function createObj (name,age,gender){
const obj = new Object()
obj.name = name
obj.age = age
obj,gender = gender
return obj
}
var obj1 = createObj('孙悟空',18,'男')
var obj2 = createObj('猪八戒',19,'女')
4.构造函数:
使用工厂化创建的对象都是object类型,就导致无法区分出类型,所以一般都是使用构造函数。构造函数使用new关键字来调用
构造函数执行流程:
①.立刻创建一个新的对象
②将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象
③逐行执行函数中的代码
④将新建的对象作为返回值返回
function Person(name,age,gender){
this.name = name
this.age = age
this.gender = gender
this.sayName = function(){
alert(this.name)
}
}
var per = new Person('孙悟空',19,'男')
var per2 = new Person('猪八戒',20,'男')
// per和per2叫做Person类的实例对象
//可以使用instanceof检查一个对象是否是一个类的实例,返回布尔值
//console.log(per instanceof Person)
5.原型对象:
1.我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype原型对象。
2.如果函数作为普通函数调用,那么这个prototype没有任何作用
3.如果函数作为构造函数调用,那么它所创建的每一个实例对象都有一个隐含的属性(proto)指向这个构造函数的prototype原型对象
4.原型对象就相当于一个公共的区域,所有同一个类的实例对象都可以访问到这个原型对象,所以我们可以将公共的东西放到这个原型对象中,这样不用分别为每个对象添加,优化了性能,也不会影响到全局作用域,就可以使每个对象都有该属性和方法
5.当我们访问对象的属性时,先从对象自身去寻找属性,当没找到时就去原型对象中寻找
6.使用in检查对象中是否有该属性,返回布尔值,如果原型对象中有该属性也会返回true:
console.log(‘name’ in person)
7.可以使用对象的hasOwnProperty()方法来检查对象自身中是否有该属性,不包括原型对象中的属性,返回布尔值
person.hasOwnProperty('name')
console.log(Person.prototype.__proto__.hasOwnProperty("hasOwnProperty"))
// hasOwnProperty方法存在原型的原型当中
6、forEach遍历数组
forEach里面的是回调函数,遍历的数组有多少个值,回调函数就会执行多少次,回调函数有三个参数,第一个参数是数组中的元素,第二个参数是数组的索引,第三个参数是正在遍历的数组
const arr = ['ashin','孙悟空','猪八戒','12']
let newArr =[]
arr.forEach((value,index)=>{
newArr.push({
index:index
name:value,
})
})
console.log(newArr)
7.map():对数组中的元素调用函数进行处理,并且把处理结果放在一个新数组中返回
var Array = [1,2,3];
var result =Array.map(function(item){
return item* 2;
});
console.log(result);//[2,4,6]
8.filter():筛选数组中符合条件的所有元素,并且放在一个新数组中
var Array= [1,2,3,4,5,6,7];
var result = Array.filter(function(value){
return value>5;
});
console.log(result);//[6,7]
9.find():用来返回数组中符合条件的第一个元素
var Array = [1,2,3,4,5,6,7];
var result = Array.find(function(value){
return value > 5; //条件
});
console.log(result);//6
10.slice(start,end):
用来截取数组中的指定元素,有两个参数,第一个是截取开始位置的索引,第二个是截取结束位置的索引,参数可以是负数,表示从后往前算,该方法不会改变原有数组,需要返回到新数组中
var result = arr.slice(1,3)
11.splice(start,number,newValue):
用于删除数组中的指定元素,splice会影响到原数组,将指定元素从原数组中删除。有两个参数,第一个参数是开始位置的索引,第二个是删除的个数,第三个及以后参数是替换的值
12.数组去重
var arr = [1,2,3,4,4,5,4,3,2,1]
for(var i =0;i<arr.length;i++){
for(var j = i+1;j<arr.length;j++){
if(arr[i]==arr[j]){
arr.splice(j,1)
j--
}
}
}
13.ES6中使用Set()去重:
var arr=['a','b',1,2,1,3];
var newarr=new Set(arr);
console.log(newarr) //输出 {"a", "b", 1, 2, 3}
let arr1=[1,2,3]
let arr2=[1,3,4]
let arr3=new Set([...arr1,...arr2]) //注意这里是对家中括号
console.log(arr3); //输出 {1, 2, 3, 4}
14.call()、apply():
两个方法都是函数对象的方法,需要通过函数对象来调用,当对函数调用call()或apply()都会调用函数执行
可以将一个对象指定为第一个参数,此时这个对象将会成为函数执行时的this。
两者的区别: call() 传递参数,依次传递;apply()传递参数要用数组传递
function fun(a,b){
console.log(this.name)
console.log('a:',a)
console.log('b',b)
}
var obj = {
name:"obj",
sayName:function(){
alert(this.name)
}
}
fun.call(obj,1,2) //obj, 'a':1 'b':2
fun.apply(obj,[1,2]) //obj, 'a':1 'b':2
15.this的情况:
1.以函数的形式调用时,this永远都是window
2.以方法的形式调用时,this是调用方法的对象,即谁调用this就指向谁
3.以构造函数调用时,this是新创建的那个对象
4.使用call()或者apply()调用时,this是指定的那个对象
16.arguments:
是一个类数组对象,它也可以通过索引操作数据,也可以获取长度,在调用函数的时候,我们所传递的实参都在arguments中保存
arguments.length可以用来获取实参的个数
即使不定义形参,也可以通过arguments使用实参;arguments[0]表示第一个实参,arguments[1]表示第二个实参
arguments里边有一个属性callee,这个属性对应一个函数对象,就是正在执行的这个函数对象
17.闭包:
正常来说,函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。而闭包就是能够读取其他函数内部变量的函数,所以说,闭包可以简单理解成“定义在一个函数内部的函数,然后return出来。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
闭包的用途:一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中,不会在外部函数f1调用后被自动清除。
function f1(){
var n=999;
function f2(){
n++
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
result(); // 1000
// 证明了函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
//为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
筛选两个数之间的所有数字
var arr = [1,2,33,44,4,55,42,3,2,1]
function between(a,b){
return function(v){
return v>a&&v<b
}
}
const res = arr.filter(between(5,100))
console.table('res:',res)
闭包this的问题
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); // The Window
var object = {
name : "My Object",
getNameFunc : function(){
return ()=>{ //使用箭头函数就可以指向父级的this。或者用This = this
return this.name;
};
}
};
alert(object.getNameFunc()()); // My Object
闭包的注意点:
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
18.数据类型
1.原始类型(基本类型、值类型):Number、String、Boolean、undefined、Null、symbol、bigInt
2.引用类型(对象):Object----》Arrary、Date、Math、Set、Map等等
原始类型和引用类型的赋值是不一样的,原始类型赋的就是一个值,而引用类型赋的是引用是一个指向,例如:
let str1 = {name:'xiaoming'}
let str2 = str1
str1.name = 'xiaohong';
//此时str2也改变了
注意:原始类型和引用类型的传参也是不一样的,原始类型作为参数,函数里面的操作不会影响到实参的值,但是引用类型传参会影响到实参的值
Promise
1.出现的意义: 解决回调地狱(在回调函数中嵌套回调)
2.promise的基本使用: promise是一个构造函数,通过new关键字实例化对象
语法:
new promise((resolve,reject)=>{})
//promise接收一个函数作为参数,
//在参数函数中接收两个参数 :resolve,reject
3.promise实例有两个属性:state:状态 result:结果
4.promise的三种状态:
第一种状态:pending(准备,待解决,进行中)
第二种状态:fulfilled(已完成,成功)
第三种状态:rejected(已拒绝,失败)
5.promise状态的改变: 通过resolve()和reject()改变当前promise对象的状态
const p = new promise((resolve,reject)=>{
resolve('参数'); //调用函数,使当前promise对象的状态改为fulfilled
reject('参数'); //调用该函数,使当前promise对象的状态改为rejected
})
promise状态的改变是一次性的,当调用了resolve()改变为fulfilled之后再调用其他就没用了
6.promise的then方法:
then方法函数有两个参数,两个参数都是函数:1.当promise的状态是fullfilled时执行;2.当promise的状态是rejected时执行,then方法里面的参数函数的形参就分别对应于resolve的参数和rejected的参数
const p = new Promise((resolve,reject)=>{
resolve('成功调用了');
//reject('失败调用了')
})
p.then((value)=>{
console.log('成功了',value) //value参数就是resolve的参数,相当于形参对应实参
},
(err)=>{
console.log('失败了',err) //err就是rejected的参数 相当于形参对应实参
})
then方法会返回一个新的promise,状态是pending
注意:
1.promise的状态没有改变,是不会执行then方法的,即promise没有执行resolve()或者reject(),状态是pending状态,即以下代码是没有执行
2.在then方法中,通过return将返回的的promise实例改为fulfilled状态,才可以接下来继续调用then方法,return的值就是下一个then的成功参数;如果在then方法中出现代码错误,会将返回的promise实例改为rejected状态
new Promise((resolve,reject)=>{
resolve('123')
}).then((value)=>{
console.log('成功',value)
return 456
},(reason)=>{
console.log('失败')
}).then((value2)=>{
console.log('成功2',value2)
},(reason)=>{
consol.log('失败了2')
})
3.catch方法:
catch方法在什么时候被执行?
1.当执行rejected()时候
2.当promise出现错误代码时
3.手动输出错误
then: 正常返回的时候,Promise状态时fulfilled;报错的时候,promise的状态是rejected
catch: 正常返回时候状态也是fulfilled,报错的时候才是rejected,而不是执行了catch就是状态为rejected
var pp = new Promise((resolve,reject)=>{
//reject();
//console.log(a)
throw new Error('出错啦!')
})
pp.catch((reason)=>{
console.log('失败了',reason)
})
console.log(pp)
promise常规写法:
new Promise((resolve,reject)=>{
// resolve()
// reject()
}).then((value)=>{
//成功时被执行
console.log(value)
}).catch((reason)=>{
//失败时执行
console.log(reason)
})
用promise解决回调地狱:
function getData(url,data={}){
return new Promise((resolve,reject)=>{
$.ajax({
method:'GET',
url:url,
data:data,
success:function(res){
//修改promise的状态为成功,修改promise的结果res
resolve(res)
},
error:function(res){
reject(res)
}
})
})
}
getData('data1.json').then((data)=>{
const {name} = data;
console.log(name)
return getData('data2.json',name)
}).then((data)=>{
const {phone} = data;
console.log(phone)
return getData('data3.json',phone)
}).then((data)=>{
const {sex} = data
console.log(sex)
})
async、await与Promise
1.执行async函数,返回的都是promise对象
async function test1(){
return 1 //自动封装乘promise对象
}
async function test2(){
retuen Promise.resolve(1)
}
2.Promise.then 成功的情况对应 await;
await后面跟promise对象
async function test3(){
const p3 = Promise.resolve(3)
p3.then(data=>{
console.log('data',data)
})
const data = await p3 // 与p3.then()是一样的,都是输出3
console.log('data',data)
}
await 后面跟一个值
async function test4(){
const data = await 4 //相当于执行了 await Promise.resolve(4) ;又因为await相当于Promise.then
console.log(data) //4
}
await后面跟异步函数
async function test5(){
const data = await test1()
console.log(data) //输出1
}
promise.catch对应try…catch
async function test6(){
const p6 = Promise.reject(6)
try{
const data6 = await p6
console.log(data6)
}catch(e){
console.log('e',e) //捕获到错误,输出6
}
}
async(异步)函数执行的顺序问题
async function test1(){
console.log('a')
const result = await test2() //await后面的代码,可以看成是放在异步里面的代码,相当于放在setTimeout里面
console.log('result:',result)
console.log('b')
}
async function test2(){
console.log('c')
}
console.log('d')
test1()
console.log(e)
//执行顺序应该为:d,a,c,e,result:underfined,b
微任务与宏任务
宏任务:setTimeout,setInterval,DOM事件,AJAX请求,DOM渲染
微任务:Promise、async/await
结论: 微任务比宏任务执行时机早; 微任务>DOM渲染>宏任务
console.log(1)
Promise.resolve().then(()=>{
console.log(2)
})
setTimeout(()=>{
console.log(3)
},0)
console.log(4)
// 执行结果为: 1,4,2,3
Event Loop
event loop: 是一个执行模型,在不同的地方有不同的实现。浏览器和NodeJS基于不同的技术实现了各自的Event Loop。
event Loop: 开始的时候,会从全局一行一行的执行,遇到函数调用,会压入到调用栈中,被压入的函数被称为帧,当函数返回后会从调用栈中弹出
消息队列: 是暂时存放异步任务的地方,等同步代码执行完毕以后,EventLoop会从消息队列中一次取出异步任务放到调用栈再次执行