1.变量提升
console.log(a);
var a = 2;
这里的 打印的结果 underfinde
console.log(a);
var a = 2;
function a(){};
这里的 a打印的是 function a(){}
是因为 js在执行过程中 预编译 -> var声明变量提升 function函数定义的变量提升
然后js由上到下执行 1的执行顺序其实是
var a ;
console.log(a);
a = 2;
2的执行顺序其实为
var a ;
function a(){};
console.log(a);
//预编译时 进行变量提升 是有个变量对象VO的 上面的其实是这样的
VO variable object 变量对象 {
a: underfined;
a: function () {}
}
// 又因为对象的键是只能唯一的 所以后面会覆盖前面 即
VO variable object 变量对象 {
a: function () {}
} 到这里预编译结束 接着执行js代码
所以这里打印的是 a函数
2.var let const的区别
let与const 是es6新增的用于定义变量 新增的块级作用域
var a = 1
function test(){
console.log(a); //这里是可以访问到外部变量 a 也就是作用域中的变量污染
}
let b = 2;
const c = 3
function test1(){
console.log(b); //这里由于访问不到外部变量会报错
console.log(c);
}
其实上面代码 相当于
{
let b = 2;//新增块级作用域概念
const c = 3
}
function test1(){
console.log(b); //这里由于访问不到外部变量会报错
console.log(c);
}
let与const的区别
let声明的变量改变值,而const声明的不能改变值
但是 const声明的对象可以改变其中的键值
const a = 1
a = 2 //会报错
const obj = {}
obj.a = 2 //可以改变 那是什么原因呢?
首先我们知道js是有简单数据类型和复杂数据类型也称引用数据类型的区别的
简单数据类型保存在栈内存当中 引用数据类型保存在堆内存中,但在栈内存也保存着指向堆内存的地址 即指针
//栈
// {
// obj: 012 //指针
// }
// 堆
// {
// 012: { a: 2 }
// }
// 所以obj变量保存的是引用类型的指针或者叫引用地址,
// 新增属性引用地址没变所以不会报错,只是改变了在堆内存中的值
3.什么是词法作用域,又称静态作用域,区别于动态作用域
var a = 1;
function fn1 () {
console.log(a);
}
function fn2 () {
var a = 2;
fn1();
}
fn2()
这里打印什么呢?答案是1
原因 因为js是静态作用域 函数执行之前就已经确认变量查找的顺序 准确的说
在函数定义的时候就已经确认了 所以这时只查找到全局a=1
而如果是动态作用域 即在真正的执行的时候才能确认变量的查找顺序
4.原型链
这部分一直不怎么清楚 举个例子
function Person(name,age){
this.name:name;
this.age:age;
}
let p = new Person();
这时候 我们都知道有
p.__proto__ = Person.prototype //实例化对象p的__proto__指向构造原型的原型对象
Person.prototype.__proto__ = Object.prototype //看一个变量的__proto__指向谁,就看这个变量是由谁生成的
Object.prototype.__proto__ = null //为了防止原型链无限往上查找 规定了
Object.__proto__ = Function.prototype // let p = new Person();
Function.prototype.__proto__ === Object.prototype
总结为
//通过构造函数new实例化生成实例化对象,实例化对象的__proto__指向的是构造函数的原型对象
//一个实例化对象的__proto__永远指向与生成这个实例化对象的构造函数的原型对象
//看一个变量的__proto__指向谁,就看这个变量是由谁生成的
那么new实例化做了什么,并且要会手写实现new ??? 这个下周开始研究
5.箭头函数和普通函数的区别
1、有无this,指向区别 ?
2、箭头函数没有arguments实参列表,类数组,类数组如何转换成数组?
3、箭头函数不能作为构建函数使用,也就是能new 箭头函数,为什么不能?
var a = 2;
var obj = {
a: 1,
fn1: function () { console.log(this.a) },
fn2: () => { console.log(this.a) }
};
console.log(obj.fn1());
console.log(obj.fn2());
这里的结果是什么呢?
这个下周开始研究
6.Event Loop js事件循环
js 的代码执行顺序 我们知道 是先执行同步任务 再执行异步任务
console.log(1);
setTimeOut(()=>{console.log(2)},0})
这里是先打印 1再打印2
同步任务是放在 运行栈中执行
异步任务是放在 任务队列执行,而事件循环就是不断检测任务队列中是否有任务,如果有就去执行
异步任务 分为微任务 宏任务
微任务 promise中的 then 注意promise中的函数是同步执行的 如
new Promise((resolve)=>{
console.log("同步执行") //这是同步执行的
resolve()
}.then(()=>{
console.log("异步执行") //异步执行 微任务
}))
宏任务 ajax 计时器 读取文件
大体的执行顺序就是 同步任务 微任务 宏任务 //node.js里面有2个方法 整理完成就是
同步任务 process.nextTick() /同步执行完,异步任务执行前/ 微任务 宏任务 setImmeDiate() /当前事件循环结束/
例如
setImmeDiate(()=>{console.log(1)});
console.log(2);
setTimeOut(()=>{console.log(3)},0);
setTimeOut(()=>{console.log(4)},200);
console.log(5);
new Promise((resolve)=>{
console.log(6);
resolve()
}.then(()=>{
console.log(7);
}))
process.nextTick(()=>{console.log(8)})
这里的打印顺序为 2,5,6,8,7,3,1,4
6.this指向
全局作用的函数 this指向全局windows对象
一般来说 函数中的this指向调用者
箭头函数 中的this指向定义函数时上下文
事件中的this指向触发事件的dom对象
方法中的this指向调用方法的对象
构造函数中的this指向new创建的对象
7.call apply bind方法
这三个方法都是用来改变this指向的
function User(){
this.name = "xiaoming"
}
function Admin(){
this.name = "xiaohong";
this.sayName = function(){
console.log(this.name);
}
}
let admin = new Admin();
admin.sayName(); // 这里肯定打印小红
function User(){
this.name = "xiaoming"
}
function Admin(){
this.name = "xiaohong";
this.sayName = function(age,like){
console.log(`我是${this.name},我的年龄是${age},我的爱好是${like}`);
}
}
let user = new User()
let admin = new Admin();
admin.sayName.call(user,18,"学习"); //使用call方法改变this指向,第一个参数是改变后的this对象,
第二个参数是继承过来的方法的参数,多个形参就传多个参数 apply方法与call方法使用方式相
同,只不过多个形参使用数组方式传递 如 admin.sayName.apply(user,[18,"学习"]).bind方法使
用方法和传递参数方式与call完全一样,只不过call方法不会自动执行,如果我们使用时需要执行的
话可以手动执行如 admin.sayName.bind(user,18,"学习")() 效果是一样的。