var 、 let
声明的就是变量const
声明的就是常量赋初始值
引用类型
,在不进行重新赋值的前提下,可以进行修改
数据
,也需要在内存中占用内存空间,保存在内存的栈结构分区中面试:let、const、var的区别
重复声明
,var是可以重复声明的变量提升
,必须先声明后使用;var 存在变量提升,可以先使用后声明块级作用域
,var没有undefined 和 null 的区别
- undefined:定义了但没有赋值;或者没有return的函数
- null:定义了且赋值为null
初始值赋值为null,表明现在数据不明,但将来会赋值为对象
结束时赋值为null:让对象成为垃圾对象,被垃圾回收器回收
当Number、String、Boolean调用方法或属性时,就会变为
基本包装类型
(引用类型)
引用数据类型的对象在堆空间
中存储,该空间的地址在栈空间中存储,如果修改栈空间存储的地址,则指向发生变化,也叫引用发生了变化,此时是在堆空间中重新指向了一个新的内存空间(存储了一个新的对象)
语法:
typeof 变量名
typeof(变量名)
数据类型
let a = 10
console.log(typeof a) //number
let b = 'hha'
console.log(typeof b) // string
let c = true
console.log(typeof c) // boolean
let d = Symbol(23)
console.log(typeof d) // symbol
let e
console.log(typeof e) // undefined
let fun = function() {}
console.log(typeof fun) // function
typeof不能判断 object和array、object和null
let f = null
console.log(typeof f) // object
let obj = {
name:'dudu'
}
console.log(typeof obj) // object
let arr = [2,3,12]
console.log(typeof arr) // object
let date = new Date()
console.log(typeof date) // object
语法
对象 instanceof 类型
等同符
,当两边值的类型相同时,直接比较值,若类型不相同,直接返回false等值符
:当等号两边的类型相同时,直接比较值是否相等,若不相同,则先转化为类型相同的值,再进行比较类型转换规则:
- 如果等号两边是boolean、string、number三者中任意两者进行比较时,优先转换为数字进行比较。
- 如果等号两边出现了null或undefined,null和undefined除了和自己相等,就彼此相等
注意:NaN==NaN //返回false NaN不等于任何值
除了判断是否等于 ==null 外,其他一律用 ===
let obj = {
name:'哈哈',
age:10
}
let obj = new Object()
obj.name = '嘟嘟'
function createObject(name,age){
var obj = new Object()
obj.name = name
obj.age = age
return obj
}
var obj1 =createObject('小明',10)
var obj2 =createObject('小红',20)
console.log(obj1,obj2)
function Person(name,gender){
this.name = name
this.gender = gender
this.sayHi=function(){
console.log('您好,我是:'+this.name)
}
}
var per = new Person('小明','男')
per.sayHi()
console.log(per)
构造器内
的属性都是在实例上
的赋值符号
定义的,则该方法在实例
上,否则该方法是在原型上 class Student{
// 构造器,构造器中的属性都是在实例上的
constructor(name,age,gender){
this.name = name
this.age =age
this.gender = gender
}
// 在原型上
sayHi(){
console.log(`您好,我是${this.name},今年${this.age}岁了,是${this.gender}生`)
}
// 在实例上
eat=()=>{
console.log('吃东西啊')
}
}
// 实例化
const stu = new Student('dudu',18,'女')
stu.sayHi()
stu.eat()
console.log(stu)
function createObj() {
let instance = null
return function(name) {
// 如果对象不存在,则新创建一个,如果已经存在则直接返回
if(!instance) {
// 创建一个对象
instance = new Object()
// 初始化对象属性
instance.name = name
}
return instance
}
}
var getObj = createObj()
var obj1 = getObj('小明')
var obj2 = getObj('小红')
console.log(obj1,obj2) // {name:小明},{name:小明}
console.log(obj1===obj2) // true
对象.属性或方法名字
对象['属性或方法名']
什么时候使用
对象[属性名字]
的写法
- 不确定属性名字是什么(
属性名字是变量
),此时方括号内部的属性名不加引号
- 属性名
不符合js命名规范
的,此时方括号内部的属性名必须加单引号
面试:谈谈你对面向对象的理解
定义
,包含函数声明或者函数表达式两种形式的定义内部的语句
被执行 /**
* 定义函数(函数声明+函数内部语句)
*/
// 函数定义方式一 :函数声明
// 执行函数定义
function fun() {
console.log('我是一个方法')
}
// 函数的调用 —— 执行函数(函数内部的语句被执行)
fun()
// 函数定义方式二:函数表达式
const fun1 = function() {}
常见的函数回调
- DOM事件的回调
- 定时器中的回调
- ajax回调函数
- 生命周期回调函数
- IIFE(立即执行函数 | 匿名函数自调用):隐藏内部实现,减少命名空间污染
执行函数定义
时就会产生显示原型(prototype)
,而函数本身也是一个实例对象,所以同时也会产生隐式原型(__ proto__)
【注:谷歌中的__proto__显示为 [[prototype]]
】 function fun() {
console.log('我是一个方法')
}
console.dir(fun)
面试:谈谈你对原型的理解
prototype(原型)
,这个属性对应着一个对象称为原型对象
显示原型prototype
__ proto__ (隐式原型)
访问该隐含属性 function Fn() {}
var obj = {}
// obj是对象(object),会有隐式原型__proto__,所以obj的__proto__和Object的prototype相等
console.log(obj.__proto__ === Object.prototype) //true
console.log(Fn.prototype.__proto__ === Object.prototype) //true
function Person(name,age) {
this.name = name
this.age = age
this.eat = function(){
console.log(`我是${name}实例中的eat方法`)
}
}
Person.prototype.sayHi = function() {
console.log('我是原型上的方法')
}
const dudu = new Person('嘟嘟',16)
const heihei = new Person('黑黑',19)
dudu.eat()
heihei.eat()
dudu.sayHi()
heihei.sayHi()
- 在构造函数中定义的属性及方法,仅仅是编写代码进行定义而已,而实际上里面定义的属性及方法是属于每个实例对象的
- 所以,创建多个对象,就会开辟多个空间,每个空间中的每个对象都有自己的属性及方法,大量创建对象,对象的方法都不是同一个方法(方法也是函数,函数代码也占用空间)
- 通过将属性或方法绑定在原型上,就可以实现所有实例的数据共享,进而节约内存空间
方法的继承
关键语句:子类.prototype = new 父类()
,这样可以使用父类的方法
// 父类
function Person(name,age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function() {
console.log(`您好,我是${this.name},今年${this.age}岁了`)
}
// 子类
function Student(name,age,studentID) {
this.name = name
this.age = age
this.studentID = studentID
}
// 实现继承的关键语句
Student.prototype = new Person('黑黑',23)
const stu = new Student('嘟嘟',18,134287)
stu.sayHi() //您好,我是嘟嘟,今年18岁了
继承属性
:父类名.call(要继承的属性名)
,这样就可以使用父类的属性 // 父类
function Person(name,age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function() {
console.log(`您好,我是${this.name},今年${this.age}岁了`)
}
// 子类
function Student(name,age,studentID) {
Person.call(name)
Person.call(age)
this.studentID = studentID
}
// 实现继承的关键语句
Student.prototype = new Person('黑黑',23)
const stu = new Student('嘟嘟',18,134287)
stu.sayHi() // 您好,我是黑黑,今年23岁了
实例可以打点访问它的原型的属性和方法
,这被称为“原型链查找
”in
检测对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回trueconsole.log('sayHi' in stu) // true
hasOwnProperty()
检测对象自身是否含有某属性,只有对象自身含有该属性才会返回trueconsole.log(stu.hasOwnProperty('sayHi')) // false
Object对象
的原型没有原型,如果在Object中仍然没有找到,则返回undefinedconstructor属性
指向对应的构造函数function sum(a,b) {
return a+b
}
console.log(sum.prototype.constructor === sum) //true
A instanceof B
- A是实例对象,B是构造函数
- 如果B的 prototype 属性所指向的原型对象是 A 原型链上的某个对象,则返回 true ,否则返回 false
// 构造函数
function Foo() {}
// 实例对象
const f1 = new Foo()
// 实例对象
const f2 = new Foo()
// 实例对象
const o1 = new Object()
// 实例对象
const o2 = {}
console.log(Foo instanceof Object); // true
console.log(Foo instanceof Function); // true
console.log(Object instanceof Object); // true
console.log(Function instanceof Function); // true
console.log(Object instanceof Foo); // flase
console.log(f1 instanceof Function); // false
console.log(f1 instanceof Object); // true
父类.call()
子类.prototype = new 父类()
子类.prototype.constructor = 子类
class 子类 extends 父类
super(属性名,属性名1,属性名2,...)
fun()
// 函数表达式
const fun = function() {}
- js引擎在js代码正式执行前会先创建一个执行环境
- 进入该环境以后会创建一个变量对象,该对象用于收集:变量,函数,函数的参数,this
- 找关键字var,function
- 确认this
- 创建作用域链
面试:谈谈你对执行上下文的理解
- 当代码要执行,但是没有执行,或者将要执行,在预解析之后,
- 此时出现了全局执行上下文环境(全局执行上下文),
- 创建了一个变量对象,用来收集var , function ,函数参数,确定this的指向,
- 默认全局执行上下文是确定了this是window,
- 这个变量对象会被压入到栈中(全局执行上下文的变量对象在栈中的最下面),
- 如果出现了函数调用,此时出现了局部执行上下文环境(局部执行上下文),
- 再次创建一个变量对象,用来收集函数参数,var ,function,改变this的指向,
- 这个变量对象会被再次压入栈中,在全局执行上下文的变量对象的上面,
- 如果当前函数调用完毕,此时出栈(把局部上下文的变量对象干掉),依次弹出变量对象,就结束了
全局作用域
:变量在函数外定义,即为全局变量
。全局变量有 全局作用域: 网页中所有脚本和函数均可使用函数作用域
:写在函数内部的变量,就只能在函数内部使用块级作用域
:写在花括号 {} 内部的变量function(){}、for(){}、while(){}、do{}while()、if(){}、switch(){}
当代码书写完毕后,全局作用域就被确定了
在执行代码之前,会先预解析,此时全局的执行上下文出现
调用函数之前,局部作用域就确定了,局部作用域中预解析执行,此时局部的执行上下文就确定了
自由变量的查找,是在函数定义的地方,向上级作用域查找,而不是在执行的地方查找!
- 函数之间存在嵌套关系
- 调用了外部函数
- 内部函数引用了外部函数的数据(变量/函数)
- 则内部函数在
执行函数定义
时,就产生了闭包。
面试:谈谈你对闭包的理解
答题方向:闭包的产生、是什么、优缺点、应用
对象打点调用函数时,this是打点的对象
对象.方法()
var obj1 = {
a:1,
b:2,
fn() {
console.log(this.a+this.b)
}
}
var obj2 = {
a:3,
b:4,
fn:obj1.fn
}
obj2.fn() // obj2调用的,所以this是obj2。然后函数也是引用类型,进行赋值的时候会指向同一个堆,所以会打印出 7
function outer() {
var a = 20
var b = 12
return {
a:33,
b:44,
fn() {
console.log(this.a+this.b)
}
}
}
outer().fn() // outer()返回一个对象,所以最后也是构成了对象.方法的调用,this就会指向返回的对象,所以会打印出77
圆括号直接调用函数,则this是window对象
函数()
var obj1 = {
a:1,
b:2,
fn() {
console.log(this.a+this.b)
}
}
var a = 3
var b = 4
var fn = obj1.fn
fn() //通过函数直接调用,this指向window对象,则结果会返回7
function fn() {
return this.a + this.b
}
var a = 1
var b =2
var obj = {
a:3,
b:fn(),
fn:fn
}
var result = obj.fn() //通过对象的形式调用,this指向obj对象,a=3,b:fn()相当于全局调用了fn方法,b中的this指向window,所以b=3
console.log(result) // 6
数组(类数组对象)枚举出函数进行调用 ,this是这个数组(类数组对象)
数组 [ 下标 ] ( )
类数组对象
:所有的键名为自然数序列(从0开始)且有length属性的对象arguments对象
是最常见的类数组对象,它是函数的实参列表function fun() {
arguments[3]()
}
fun('A','B','C',function() {
console.log(this[1]) // this指向arguments类数组对象'A','B','C',function(),结果输出B
})
4 规则四:IIFE中的函数,this是window对象
(function() {
…
})()
var a = 1
var obj = {
a:2,
fun:(function(){
var a = this.a; //IIFE中的this指向window,则a=1
return function() {
// a 在函数体内部,则a=1
// return 返回了一个函数
console.log(a + this.a) // 3
}
})()
}
obj.fun() //点的方式调用函数,则this指向obj对象,则this.a=2
定时器、延时器调用函数,this是window对象
setInterval(函数,时间)
setTimeout(函数,时间)
var obj = {
a:1,
b:2,
fun() {
console.log(this.a + this.b)
}
}
var a = 3
var b = 4
setTimeout(obj.fun, 2000); //this指向window,则this.a=3,this.b=4
setTimeout(() => {
obj.fun() //对象点方法的形式调用函数,则this指向obj对象,this.a=1、this.b=2
}, 2000);
事件处理函数的this是绑定事件的DOM元素
DOM元素.onclick = function() {
//当函数内部有其他函数,比如定时器时,要考虑this的指向
// 必要时需要备份this,即 var _this = this,这时在内部的函数中就可以用_this指向需要的DOM
}
call和apply可以指定 this 的指向
函数.call(上下文)
函数.apply(上下文)
规则八: 构造函数中,this指向实例对象
规则九: 箭头函数中,this会向上级作用域寻找
1. 同步任务
非耗时任务
,指主线程上排队执行的任务耗时任务
,指由js委托给**宿主环境
**(js的执行环境,如浏览器、node.js等)进行执行回调函数 callback
**异步任务
分为宏任务和微任务
宏队列
:用来存放宏任务的容器微队列
:用来存放微任务的容器构造函数
**,可以通过new创建一个实例,new出来的Promise实例对象就代表一个异步操作原型对象(prototype)
上包含一个 .then()
方法then()方法用来指定成功和失败的回调函数
fun.then(成功的回调,失败的回调)
状态的改变不可逆,只能从进行中变为成功或失败
new Promise((resolve,reject) => {
// 成功的回调
// resolve('成功!')
// 失败的回调
reject('失败~')
}).then(val => {
console.log(val)
},err => {
console.log(err)
// 抛出错误
throw 100
}).then(val => {
console.log('success '+val)
},err => {
console.log('failed '+err)
})
new Promise((resolve,reject) => {
// 成功的回调
// resolve('成功!')
// 失败的回调
reject('失败~')
}).then(val => {
console.log(val)
},err => {
console.log(err)
// 抛出错误
// throw 100
// 返回失败的回调
return Promise.reject(200)
}).then(val => {
console.log('success '+val)
},err => {
console.log('failed '+err)
})
new Promise((resolve,reject) => {
// 成功的回调
// resolve('成功!')
// 失败的回调
reject('失败~')
}).then(val => {
console.log(val)
},err => {
console.log(err)
// 抛出错误
// throw 100
// 返回失败的回调
// return Promise.reject(200)
// 返回成功的回调
return Promise.resolve(300)
}).then(val => {
console.log('success '+val)
},err => {
console.log('failed '+err)
})
new Promise((resolve,reject) => {
// 成功的回调
// resolve('成功!')
// 失败的回调
reject('失败~')
}).then(val => {
console.log(val)
},err => {
console.log(err)
// 抛出错误
// throw 100
// 返回失败的回调
// return Promise.reject(200)
// 返回成功的回调
// return Promise.resolve(300)
// 返回其他值
return 1000
}).then(val => {
console.log('success '+val)
},err => {
console.log('failed '+err)
})
Promise.all([Promise1,Promise2,…]) 方法可以把多个Promise实例包装为一个新的Promise实例
只有需要包装的多个Promise实例状态都为成功,新的Promise状态才为成功(等待机制)
const promiseArr = [
thenFs.readFile('./files/1.txt','utf-8'),
thenFs.readFile('./files/2.txt','utf-8'),
thenFs.readFile('./files/3.txt','utf-8')
]
Promise.all(promiseArr)
.then(result => {
console.log(result) //[ '第一个文件', '第二个文件', '第三个文件' ]
})
Promise.race([promise1,promise2,…])用法和all()类似,
但是只要其中任何一个异步完成,就会立即执行下一步的.then()操作(赛跑机制)
Promise.race(promiseArr)
.then(res => {
console.log(res) //第一个文件
})
用于简化Promise异步操作
async function getAllFile() {
const res1 = await thenFs.readFile('./files/1.txt','utf8')
console.log(res1)
const res3 = await thenFs.readFile('./files/3.txt','utf8')
console.log(res3)
const res2 = await thenFs.readFile('./files/2.txt','utf8')
console.log(res2)
}
getAllFile()