-
JS的内置类型
js的内置类型分为两大类:一类是JS的基本类型包括六种
- null
- undefined
- string
- number
- boolean
- symbol
第二类是引用类型Object
面试题 1.说下你知道的JS的内置类型
2.JS继承
JS继承有7种方式:
- 原型链继承
function father() { this.type = 1 } function person(name) { this.name = name this.color = 1 } person.prototype = new father() let child1 = new person('child') let child2 = new person('child') child1.__proto__.type = 2 console.log(child1.type) // 2 console.log(child2.type) // 2 // 特点:实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性 // 缺点:所有属性都会被继承,且子元素修改一个一起都被修改,所以构造函数继承就会好一点
- 构造函数继承
function Person(name) { this.name = name this.type = 'Function', this.getType = function() { return this.name } } function child() { Person.call(this, 'sun') this.age = 12 } console.log(p1.name, p1.type, p1.getType) // 特点:虽然可以像父类传参了 // 缺点:可以向父类传参了,但是每次都要调用一次父类构造器
- 组合继承
function Person(name) { this.name = name } Person.prototype.type = 'Function' Person.prototype.getType = function() { return this.name } function child() { Person.call(this, 'sun') this.age = 12 } child.prototype = new Person() let p1 = new child() console.log(p1.name, p1.type, p1.getType) // 特点: 既可以像父类传参,也可以真正意义上的继承方法 // 缺点:俩次调用父类构造函数耗性能
- 原型式继承
function f(obj) { function child() {} child.prototype = obj return new child() } let father = { color: "yellow" } let child1 = new f(father) let child2 = new f(father) child1.color = "red"console.log(child1.color) // red console.log(child2.color) // yellow console.log(father.color) // yellow // 特点:通过指定prototype来实现继承,会继承原型上的属性 但是无法实现复用
- 寄生继承
function f(obj) { function child() {} child.prototype = obj return new child() } let father = { color: "yellow" } // 再定义一个函数把对象构建放在这个里面,然后给这个对象添加公共的属性 function last() { let child = new f(father) child.age = 12 return child } let child1 = new last() console.log(child1.color, child1.age) // yellow 12
- 寄生组合式继承
function f(superObj) { function child() {} child.prototype = superObj.prototype return new child() } // 父亲 function father(name) { this.name = name } // 孩子 function child(name, age) { father.call(this, name) this.age = age } // 下面实现继承俩个对象的继承 function jicheng(child, father) { let zhongjian = new f(father) zhongjian.constructor = child child.prototype = father return zhongjian }
- class继承
class Point { constructor(x, y) { this.x = x; this.y = y; this.type = 'class' } hello() { console.log('hello world'); } } class ColorPoint extends Point { constructor(x, y, color) { super(x, y); this.color = color; // 正确 } } let p1 = new ColorPoint(1, 2, 'red') console.log(p1)
3.作用域
我理解的作用域是分为全局作用域和函数作用域,全局作用域和函数作用域其实比较好理解,声明在某个函数内的就是函数作用域,不在函数内声明的就是全局作用域,与之相关的作用域链可以理解为向下传递值的包含关系
4.变量提升
我理解的变量提升是一个变量在声明之前被访问会不会报错
对于全局作用域来说,具有var标记的变量标识符会被提升到全局作用域,同时为其赋值undefined,具有let标记的不会提升,只有在声明之后才会被访问,具有const标记的不会被提升,在运行中也不能改变其值
对于函数作用域来说,具有var标记的变量会被提升到函数作用域的顶部,同时为其赋值undefined,具有let标记的不会被提升,只有在声明之后才会被访问,具有const标记的不会被提升,在运行这不能改变其值
5.this指向问题
- 分辨this指向哪里
对于this指向我们可以从函数分类及其调用方式理解:
对于全局作用域来说,其this指向window
对于普通函数来说,this指向调用它的对象,无调用则指向window
箭头函数是一类特殊的函数,其this指向最后一个调用它的非箭头函数
匿名函数的this指向window
对于new的构造函数来说,this指向通过new创建的对象
- 如何改变this指向
apply、call、bind
var person = { voice: '11', say: function(voice1) { console.log(this.voice, voice1) } } var femal = { voice: '222' } // call person.say.call(femal,333) // 222 333 // apply person.say.apply(femal,[444]); // 222 444 // bind person.say.bind(femal); // 什么都不输出 // 从上面的例子可以看出,apply和call的作用是一样的,bind的还是不一样的 // 区别就是参数传递的方式不一样,call更简洁一些,而且call的性能更好一点 this.num = 9; var myModule = { num: 81, getNum: function() { console.log(this.num); } } myModule.getNum();
- 手写apply call bind
参考另一篇博客:https://juejin.cn/post/6904611980124880910
6.立即执行函数
我理解的立即执行函数就是函数声明后会被作为表达式立即执行
7.instanceof如何理解
我理解的a instanceof b是判断一个对象a的原型链上是否存在b对应的构造函数
比如:下面的示例就是在做判断
function person(name, age) { this.name = name this.age = age } let p1 = new person('sun', 23) // console.log(p1 instanceof person)
手写instanceof
function f(left, right) { let prototype = right.prototype left = left._proto_ while(left) { if(left == prototype) { return true }else { left = left._proto_ } } }
8.闭包
我理解的闭包是一个函数内定义了另外一个函数,这个函数会访问包裹函数中的变量,这一使用称为闭包,常见的使用闭包的有防抖和节流,注册回调,封装私有变量