1. var和let
- JS中作用域有:全局作用域、函数作用域。没有块作用域的概念。ECMAScript 6(简称ES6)中新增了块级作用域
- 块作用域由 { } 包括, if 语句和 for 语句里面的 { } 也属于块作用域
1.1 ES5问题
1.1.1 全局变量
- 在if或者for循环中声明的变量会变成全局变量
for(var i=0;i<=5;i++){
console.log("hello");
}
console.log(i); //5
1.1.2 内层变量可能会覆盖外层变量
var a = 1;
function fn() { console.log(a); if (false) {var a = 2;}
}
fn(); //undefined
1.2 let
- 允许块级作用域任意嵌套
- 外层作用域无法读取内层作用域的变量
- 内层作用域可以定义外层作用域的同名变量
- 函数本身的作用域在其所在的块级作用域之内
'use strict'
function fn() {console.log("out");
}
(function () {if (false) {function fn() {console.log("in"); }
} fn();
}());
1.3 var&let&const
- var定义的变量没有块的概念,可以跨块访问,不能跨函数访问,有变量提升,可重复声明
- let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问,无变量提升,不可以重复声明
- let 声明的变量只在块级作用域内有效,不存在变量提升,而是绑定在暂时性死区
- 或者说let变量提升了,但是在let声明变量前不能使用该变量,这特性叫暂时性死区(temporaldead zone)
- 如果有重复变量 let 会在编译阶段报错
1.3.1 暂时性死区
// 不存在变量提升
'use strict';
function func(){console.log(i);let i;
};
func(); // 报错
1.3.2 全局变量
- ES5声明变量只有两种方式:var和function
- ES6有let、const、import、class再加上ES5的var、function共有六种声明变量的方式
- 浏览器环境中顶层对象是window,Node中是global对象
- ES5中 顶层对象的属性等价于全局变量
- ES6中var、function声明的全局变量,依然是顶层对象的属性;let、const、class声明的全局变量不属于顶层对象的属性
2. this
- 当前函数的this是在被调用的时候才能确定的
- 如果当前的执行上下文处于调用栈的栈顶,这个时候变量对象变成了活动对象,THIS指针才能确定
2.1 全局对象
- 全局对象this指向本身
var a=1;//声明绑定变量对象,但在全局环境中,变量对象就是全局对象
this.b=2;//this绑定全局对象
c=3;//赋值操作 隐式绑定
2.1 用点调用
- 在一个函数上下文中,this由函数的调用者提供,由调用函数的方式来决定指向
- 如果是函数执行,如果前面有点,那么点前面是谁 this 就是谁
let obj = {getName(){console.log(this);}
};
obj.getName();
2.2 直接调用
- 如果没有,this就是window(严格模式下是undefined),自执行函数中的this一般都是window
let obj = {getName(){ console.log(this); }
};
let getName = obj.getName;
getName();
2.3 绑定事件
- 给元素绑定事件的时候,绑定的方法中的this一般是元素本身
container.addEventListener('click',function(){console.log(this);
});
2.4 箭头函数
- 箭头函数没有自己的this
- 也没有prototype
- 也没有arguments
- 无法创建箭头函数的实例
let fn = () => {console.log(this);console.log(arguments);//Uncaught ReferenceError: arguments is not defined
}
console.log(fn.prototype);//undefined
fn();
new fn();//VM4416:8 Uncaught TypeError: fn is not a constructor
2.5 构造函数
- 构造函数中的THIS是当前类的实例
function fn(){}
let obj = new fn();
2.6 call/apply/bind
- call/apply/bind可以改变函数中this的指向
- 第一个参数是改变this指向(非严格模式下,传递null/undefined指向也是window)
- call参数是依次传递,apply是以数组的方式传递
!function (proto) { function getContext(context) {context = context || window;var type = typeof context;if (['number', 'string', 'boolean', 'null'].includes(type)) {context = new context.constructor(context); } return context;} function call(context, ...args) { context = getContext(context); context._fn = this; let result = context._fn(...args); delete context._fn; return result; } function apply(context, args) {context = getContext(context);context._fn = this;let result = context._fn(...args);delete context._fn; return result;
} function bind(context, ...bindArgs) {return (...args) => this.call(context, ...bindArgs, ...args);
} proto.call = call; proto.apply = apply; proto.bind = bind;
}(Function.prototype)
2.7绑定
- 默认绑定
- 隐式绑定
- 显式绑定
- new绑定
- new > 显式 > 隐式 > 默认
隐式 > 默认
function one() {console.log(this)
}
var obj = { name: "obj", one
}
obj.one()
显式 > 隐式
function one() {console.log(this)
}
var obj = {name: "obj",one: one.bind("hello")
}
obj.one()
new > 显式
function one() {console.log(this)
}
var helloOne = one.bind("hello")
var obj = new helloOne();
console.log(obj);
最后
整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。
有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享
部分文档展示:
文章篇幅有限,后面的内容就不一一展示了
有需要的小伙伴,可以点下方卡片免费领取