一、JS数据类型有哪些
ES6之前
基本的:数字number、字符串string、布尔值boolean
表示空/无的:null和undefined
表示对象:object
ES6后新加的: symbol和bigInt
二、原型链是什么
1. 概念
假设我们有一个普通对象obj={},这个obj会有一个隐藏属性,叫__proto__,这个属性会指向Object.prototype,即
obj.__proto__ = Object.prototype
此时,我们说obj的原型是Object.prototype,或者说Object.prototype是obj的原型。而这个隐藏属性的作用就是用来指向对象的原型的。
假设我们有一个数组对象arr=[],这个arr也会有一个隐藏属性,这个隐藏属性会指向Array.prototype,即
arr.__proto__ = Array.prototype
同时,这个Array.prototype也有一个隐藏属性,指向了Object.prototype,即
Array.prototype.__proto__ = Object.prototype
这样一来,arr就有两层原型,通过隐藏属性形成原型链。
2. 实现方法
普通做法:
a = {}, b = {}
a.__proto__ = b
b.__proto__ = Object.prototype
推荐做法:
a = Object.create({}) // ES6
a = new 构造器() // ES5
a.__proto__ = 构造器名.prototype
3. 解决问题
在ES6之前,没有class的情况下实现继承。以刚刚的arr.__proto__ ==> Array.prototype ==> Object.prototype为例,我们说:
arr是Array的实例,arr拥有Array.prototype的属性Array继承了Object- 所以
arr是Object的间接实例,也拥有Object.prototype里的属性 这样一来,arr就拥有Array.prototype和Object.prototype的属性
4. 优缺点
优点:简单、优雅
缺点:跟class相比,不支持私有属性
5. 解决缺点
使用ES6的class (在属性前加#来声明私有属性)
三、this是什么
var length = 1;
function callback(){
console.log(this.length)
}
const obj = {
length: 2,
myMethod(callback){
callback()
}
}
obj.myMethod(callback); // 1
四、new是什么
- 创建临时对象
- 绑定原型(共有属性)
- 指定
this = 临时对象(this.__proto__ = 构造函数.prototype) - 执行构造函数 (自有属性)
- 返回临时对象
五、立即执行函数是什么
- 概念:声明一个函数并立即执行。
- 实现方法:
( function(){console.log('我是立即执行函数')}() ) //用括号把整个表达式包起来
( function(){console.log('我是立即执行函数')} )() //用括号把整个函数包起来
- 解决问题:在ES6之前创建局部作用域
- 优点:兼容性好(ES3都可以使用)
- 缺点:语法比较丑
- 解决缺点:使用ES6的
块+let的语法
六、闭包是什么
- 概念:闭包是JS的一种语法特性(有很多语言也支持闭包),是一个能够存取包含作用域的函数。
- 实现方法:
{
let count = 0; // 不能是全局变量,不能是局部变量。要是自由变量
function add(){
count += 1;
}
}
const add = function() {
var count = 0;
return () => { count += 1}
} ()
- 解决问题:一、避免污染全局环境(使用局部变量)。二、提供对局部变量的间接访问。三、维持变量,使其不被垃圾回收。
- 优点:简单、好用
- 缺点:闭包使用不当可能造成内存泄露。IE浏览器以前有个bug,
function test(){
let x = 1;
let y = 2;
return () => { console.log(x) }
}
const myFn = test();
const myX = myFn();
对于一个正常的浏览器来说,y会在一段时间后自动消失(被垃圾回收器回收掉)。但是旧版本IE并不会。
6. 解决缺点:少用,慎用
七、JS如何实现类
方法一、使用原型
function Dog(name,kind){
this.name = name;
this.kind = kind;
this.legsNumber = 4;
}
Dog.prototype = {
constructor: Dog, // 注意:这种写法要加上构造器属性
species: 'dog',
bark: () => { console.log('bark!'); }
}
方法二、使用class
class Dog{
static species = 'dog';
legsNumber = 4; // 等价于在constructor里写this.legsNumber = 4
constructor(name, kind){
this.name = name;
this.kind = kind;
}
bark() {
console.log('bark!');
}
}
八、JS如何实现继承
方法一、使用原型
function Animal(legsNumber){
this.legsNumber = legsNumber;
}
function Dog(name,kind){
Animal.call(this, 4);
this.name = name;
this.kind = kind;
}
Dog.prototype = new Animal();
// 假如不想要Animal返回的对象上的legsNumber属性到prototype上,可以使用以下方式
const EmptyAnimal = function(){};
EmptyAnimal.prototype = Animal.prototype;
Dog.prototype = new EmptyAnimal();
Dog.prototype.species = 'dog';
Dog.prototype.bark = () => { console.log('bark!'); };
方法二、使用class
class Animal{
constructor(legsNumber){
this.legsNumber = legsNumber;
}
}
class Dog extends Animal{
static species = 'dog';
constructor(name, kind){
super(4); // 必须先于其他语句执行
this.name = name;
this.kind = kind;
}
bark() {
console.log('bark!');
}
}