西门吹雪 2019-07-01
前两天总结了一下HTML+CSS方面的面试题 (传送门),今天翻看了一些 JavaScript
面试中常见的几个问题(只是一部分,会持续更新),分享给有需要的小伙伴,欢迎star
关注
如果文章中有出现纰漏、错误之处,还请看到的小伙伴留言指正,先行谢过
以下 ↓
6种原始数据类型:
true
和 false
Null
类型只有一个值: Null
,特指对象的值未设置undefined
Symbols
)是ECMAScript第6版新定义的。符号类型是唯一的并且是不可修改的引用类型:Object
详见 JavaScript中的数据类型
typeof
操作符:返回一个字符串,表示未经计算的操作数的类型typeof
操作符对于简单数据类型,返回其本身的数据类型,函数对象返回function
,其他对象均返回Object
Null
返回Object
A instanceof B
,返回一个Boolean
类型的值instanceof
检测的是原型,只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型
let a = []; a instanceof Array // true a instanceof Object // true
变量a 的__proto__
直接指向Array.prototype
,间接指向Object.prototype
,所以按照instanceof
的判断规则,a 就是Object
的实例.针对数组的这个问题,ES5 提供了Array.isArray()
方法 。该方法用以确认某个对象本身是否为 Array 类型
prototype
原型,然后再在 prototype
上添加一个 constructor
属性,并让其指向该函数的引用Null
和undefined
是无效的对象,因此是不会有constructor
存在的,这两种类型的数据需要通过其他方式来判断函数的
constructor
是不稳定的,这个主要体现在自定义对象上,当开发者重写prototype
后,原有的constructor
引用会丢失,constructor
会默认为Object
function F() {}; var f = new F; f.constructor == F // true F.prototype = {a: 1} var f = new F f.constructor == F // false
在构造函数F.prototype
没有被重写之前,构造函数F
就是新创建的对象F
的数据类型。当F.prototype
被重写之后,原有的constructor
引用丢失, 默认为 Object因此,为了规范开发,在重写对象原型时一般都需要重新给
constructor
赋值,以保证对象实例的类型不被篡改
Object
的原型方法,调用该方法,默认返回当前对象的 [[Class]]
。这是一个内部属性,其格式为 [object Xxx]
,其中 Xxx
就是对象的类型Object.prototype.toString.call('') ; // [object String] Object.prototype.toString.call(11) ; // [object Number] Object.prototype.toString.call(true) ; // [object Boolean] Object.prototype.toString.call(Symbol()); //[object Symbol] Object.prototype.toString.call(undefined) ; // [object Undefined] Object.prototype.toString.call(null) ; // [object Null] Object.prototype.toString.call(new Function()) ; // [object Function] Object.prototype.toString.call([]) ; // [object Array]
Null
表示"没有对象",即该处不应该有值
典型用法:
undefined
表示"缺少值",就是此处应该有一个值,但是还没有定义
典型用法:
undefined
undefined
undefined
undefined
修改器方法:
访问方法:
迭代方法:
undefined
true
,否则返回 falsetrue
,否则返回 falsetrue
的数组元素放进一个新数组中并返回更多方法请参考 MDN 传送门
对象字面量
var obj = {}
Object 构造函数
var obj = new Object()
工厂模式
function Person(name, age) { var o = new Object() o.name = name; o.age = age; o.say = function() { console.log(name) } return o }
缺点: 每次通过Person
创建对象的时候,所有的say
方法都是一样的,但是却存储了多次,浪费资源
构造函数模式
function Person(name, age) { this.name = name this.age = age this.say = function() { console.log(name) } } var person = new Person('hello', 18)
构造函数模式隐试的在最后返回return this
所以在缺少new
的情况下,会将属性和方法添加给全局对象,浏览器端就会添加给window
对象,可以根据return this
的特性调用call
或者apply
指定this
原型模式
function Person() {} Person.prototype.name = 'hanmeimei'; Person.prototype.say = function() { alert(this.name); } Person.prototype.friends = ['lilei']; var person = new Person();
实现了方法与属性的共享,可以动态添加对象的属性和方法。但是没有办法创建实例自己的属性和方法,也没有办法传递参数
构造函数和原型组合
function Person(name, age) { this.name = name this.age = age } Person.prototype.say = function() { console.log(this.name) } var person = new Person('hello')
还有好几种模式,感兴趣的小伙伴可以参考 红宝书,你们肯定知道的了!
浅拷贝
Array.prototype.slice()
也可以完成对一个数组或者对象的浅拷贝Object.assign()
方法深拷贝
JSON.parse(JSON.stringify(目标对象)
,缺点就是只能拷贝符合JSON
数据标准类型的对象详见 JavaScript中的浅拷贝与深拷贝
简单来说,闭包就是能够读取其他函数内部变量的函数
function Person() { var name = 'hello' function say () { console.log(name) } return say() } Person() // hello
由于 JavaScript 特殊的作用域,函数外部无法直接读取内部的变量,内部可以直接读取外部的变量,从而就产生了闭包的概念
用途:
最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中
注意点:
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露
首先明确一点,JavaScript是基于原型的
每个构造函数(constructor)都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针,而实例(instance)都包含一个指向原型对象的内部指针.
图解:
prototype
属性,这个属性指向一个对象,也就是原型对象constructor
属性,指向指向它的那个构造函数[[prototype]]
,指向它的原型对象那么什么是原型链:
JavaScript
中所有的对象都是由它的原型对象继承而来。而原型对象自身也是一个对象,它也有自己的原型对象,这样层层上溯,就形成了一个类似链表的结构,这就是原型链所有原型链的终点都是
Object
函数的prototype
属性。Objec.prototype
指向的原型对象同样拥有原型,不过它的原型是Null
,而Null
则没有原型
详见 JavaScript中的原型与原型链
function Animal() {} Animal.prototype.name = 'cat' Animal.prototype.age = 1 Animal.prototype.say = function() {console.log('hello')} var cat = new Animal() cat.name // cat cat.age // 1 cat.say() // hello
最简单的继承实现方式,但是也有其缺点
new
语句之后执行,不能放到构造器中function Animal() { this.species = "动物" } function Cat(name, age) { Animal.call(this) this.name = name this.age = age } var cat = new Cat('豆豆', 2) cat.name // 豆豆 cat.age // 2 cat.species // 动物
使用call或apply方法,将父对象的构造函数绑定在子对象上.
function Animal() { this.species = "动物" } function Cat(name){ Animal.call(this) this.name = name } Cat.prototype = new Animal() // 重写原型 Cat.prototype.constructor = Cat
如果没有Cat.prototype = new Animal()
这一行,Cat.prototype.constructor
是指向Cat
的;加了这一行以后,Cat.prototype.constructor
指向Animal
.这显然会导致继承链的紊乱(cat1明明是用构造函数Cat生成的),因此我们必须手动纠正,将Cat.prototype
对象的constructor
值改为Cat
extends
继承ES6新增继承方式,Class 可以通过extends关键字实现继承
class Animal { } class Cat extends Animal { constructor() { super(); } }
使用extends
实现继承,必须添加super
关键字定义子类的constructor
,这里的super()
就相当于Animal.prototype.constructor.call(this)
当然,还有很多种实现继承的方式,这里就不多说了。然后,再推荐一波 红宝书
详见 JavaScript中的继承
这里现在只是JavaScript
面试题中的一部分,后面是持续更新, 有需要的小伙伴可以关注哦
好了,周末愉快 [啊!结束了……]