第四篇,JavaScript面试题汇总
JavaScript是一种属于网络的脚本语言,已经被广泛用于web实用开发,常用来为网页添加各种各样的动态功能,为用户提供更流畅美观的浏览效果。通常JavaScript脚本是通过嵌入在HTML中来实现自身的功能的。
-
JavaScript的三大组成部分是:
- ECMAScript:JavaScript的核心,描述了语言的基础语法(var、for、if、array等)和数据类型(数字、字符串、布尔、函数、对象、数组、未定义、null),ECMAScript是一套标准,定义
了一种语言是什么样子. - DOM(document Object model):DOM(文档对象模型)是HTML和XML的应用程序接口(API)。DOM将整个页面规划成由节点 层级构成的文档。HTML 或 XML 页面的每个部分都是
一个节点的衍生物。 - BOM(browser Object model):BOM(浏览区对象模型)对浏览区窗口进行访问操作。例如弹出新的浏览器窗口,移动、变化和关闭浏览器窗口,提供详细的网络浏览器信息
(navigator object),详细的页面信息(location object),详细的用户屏幕分辨率的信息(screen object),对cookies的支持等等。BOM作为JavaScript的一部分并没有相关标准的支持,
每一个浏览器都有自己的实现,虽然有一些非事实的标准,但还是给开发者带来一定的麻烦。
- ECMAScript:JavaScript的核心,描述了语言的基础语法(var、for、if、array等)和数据类型(数字、字符串、布尔、函数、对象、数组、未定义、null),ECMAScript是一套标准,定义
-
介绍JavaScript的基本数据类型。
undefined、null、boolean、number、string -
类型判断用到哪些方法?
- typeof xxx
得到的值有以下几种类型:undefined、boolean、number、string、object、function、symbol,比较简单,不一一演示。
需要注意三点:
typeof null 结果是object,实际这是typeof的一个bug。null是原始值,非引用类型
typeof [1,2,3]结果是object,因为结果中没有array这一项,引用类型除了function其他都是Object
typeof Symbol()结果是 symbol,这是ES6新增的知识点 - instanceof
用于实例和构造函数的对应。例如判断一个变量是否是数组,使用typeof无法判断,但可以使用 [12,23] instanceof Array来判断。因为 [1,2] 是数组,它的构造数就是Array。
同理:
function Foo(name){
this.name = name
}
var foo = new Foo('bar')
console.log(foo instanceof Foo) // true
- typeof xxx
-
JavaScript有几种类型的值?,你能画一下他们吗的内存图吗?
- 栈:原始数据类型(undefined,null,boolean,number,string)
- 堆:引用数据类型(对象、数组和函数)
两种类型的区别是:储存位置不同。 - 原始数据类型存储在栈(stack)中的简单数据段,占据空间小、大小固定、属于频繁使用数据,所以放入栈中储存;
- 引用数据类型储存在堆(heap)中的对象,占据空间大、大小不固定、如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中储存了指针,该指针指向堆中该实体的起始地址。
当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
在参数传递方式上,原始类型是按值传递,引用类型是按共享传递。JavaScript中这种设计的原来是:按值传递的类型,复杂一份存入栈内存,这类类型一般不占用太多内存,而且按值传递保
证了其访问速度。按共享传递的类型,是复杂其引用,而不是整个复杂其值(c语言中的指针),保证过大的对象等不会因为不停复杂内容而造成内存的浪费。
-
介绍js有哪些内置对象?
Object是JavaScript中所有对象的父对象
数据封装类对象:Object Array Boolean Number 和 String
其他对象:Function Arguments Math Date RegEx Error -
如何区分数组好对象?
- 从原形入手,Array.protopype.isPrototypeOf(obj) 利用isPrototypeOf方法,判断Array是不是obj的原型链中,如果是,则返回true,否则false。
Array.prototype.isPrototype([]); // true - 也可以从构造函数入手,利用对向的constructor属性
- 根据对象的class属性(雷属性),跨原形链调用toString()方法。
Object.prototype.toString.call(window); - Array.isArray() 方法
- 从原形入手,Array.protopype.isPrototypeOf(obj) 利用isPrototypeOf方法,判断Array是不是obj的原型链中,如果是,则返回true,否则false。
-
undefined和null的区别
- null 表示一个对象被定义了,值为空值;
2.undefined 表示不存在这个值。
typeof undefined //undefined
是表示‘无’的原始值或者说表示‘缺少值’,就是此处应该有一个值,但是还没有定义。当尝试读取是会返回 undefined;
例如变量声明了,但还没有赋值是,就等于undefined;
typeof null //object
是表示一个对象(空对象)没有任何属性和方法;
例如作为函数的参数,表示该函数的参数不是对象;
注意:
在验证nul时,一定要使用 ===;因为 == 无法分别unll和 undefined
undefined表示‘缺少值’,就是此次应该有一个值,但是还没有定义。
典型用法是:
1)变量被声明了,但没有赋值时,就等于undefined。
2)调用函数时,应该提供的参数没有提供,改参数等于undefined。
3)对象没有赋值的属性,该属性的值为undefined。
4)函数没有返回值时,默认返回undefined。
null表示‘没有对象’,即该处不应该有值。
典型用法是:
1)作为函数的参数,表示该函数的参数不是对象。
2)作为对象原型链的终点。
- null 表示一个对象被定义了,值为空值;
-
数组和对象有哪些原生方法,列举一下?
-
字符串有哪些原生方法,列举一下?
-
声明变量和声明函数的提升有什么区别?
- 变量声明提升:变量声明在进入执行上下文就完成了。
只要变量在代码中进行了声明,无论它在那个位置上进行声明,js引擎都会将它的声明放在范围作用域的顶部; - 函数声明提升:执行代码之前会先读取函数声明,意味着可以把函数声明放在调用它的语句后面。
只要函数在代码中进行了声明,无论它在那个位置上进行声明,js引擎都会将它的声明放在范围作用域的顶部; - 变量or函数声明: 函数声明会覆盖变量声明,但不会覆盖变量赋值。
同一个名称标识a,即有变量声明var a,又有函数声明function a(){},不管二者声明的顺序,函数声明会覆盖变量声明,也就是说此时a的值是声明的函数function a(){}。
注意:
如果在变量声明的同时初始化a,或是之后在对a进行赋值,此时a的值变量的值。eg:var a;var c = 1;a = 1;function a(){return true;} console.log(a); //1
- 变量声明提升:变量声明在进入执行上下文就完成了。
-
== 和 === 的区别
12.原形 ,原型链
1. JavaScript原型,原型链? 有什么特点?
原型
每个对都会在内部初始化一个属性,就是prototype(原型)
使用hasOwnProperty()可以判断这个属性是不是对象本身的属性
问题: JavaScript中,有一个函数,执行对象查询时,永远不会查找原型,这个函数是?
hasOwnProperty
JavaScript中hasOwnProperty函数方法是返回一个布尔值,指出一个对象是否具有指定名称的属性。
此方法无法检查该对象的原型链中是否具有该属性;该属性必须是对象本身的一个成员。
使用方法:
object.hasOwnProperty(proName)
其中参数object是必须选项。一个对象的实现。
proName是必选项。一个属性名称的字符串值。
如果object具有指定名称的属性,那么JavaScript中hasOwnProperty函数方法返回true,反之则返回false。
原型链
当我们在访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,
这个prototype又会有自己的protptype,于是就这样一直找下去,找到Object为
止,找不到就返回undefined也就是我们平时所说的原型链的概念。
关系: instance.constructor.prototype = instance.proto
特点:JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型链时,与之相关的对象也会继承这一改变。
当我们需要一个属性的时,JavaScript引擎会先看当前对象中是否有这个属性,如果没有的话就会找他的Prototype对象是否有这个属性,如此递推下去,一直检索到Object内建对象。
所有的引用类型(数组、对象、函数),都具有对象特征,即可自由扩展属性(null除外)
所有的引用类型(数组、对象、函数),都有一个__proto__属性,属性值是一个普通的对象
所有的函数,都有一个prototype属性,属性值也是一个普通的对象。
所有的引用类型(数组、对象、函数),__proto__属性值指向它的构造函数的prototype属性值
原型链中的this
所有从原型或更高级原型中得到、执行的方法,其中的this在执行时,就指向了当前这个触发事件执行的对象。
闭包
闭包的形成与变量的作用域以及变量的生命周期有密切的关系
1. 变量的作用域
在js中我们把作用域分为全局作用域和局部作用域,全局作用域就是window,在没有快级作用域概念的时候,每一个函数都是一个局部作用域。
其实变量的作用域,就说指变量的有效范围。我们最常说的就是在函数中声明的变量作用域。
当在函数中声明一个变量的时候,如果该变量没有用var关键字去定义那么该变量就是一个全局变量,但是这样做最容易造成命名冲突。
另一种情况就是使用var声明的变量,这时候的变量就是局部变量,只有在函数内部可以访问,在函数外面是访问不到的。
在JavaScript中,函数可以用来创造函数作用域。在函数中搜索变量的时候,如果该函数当中没有这个变量,那么这次搜索过程会随着代码执行环境创建的作用域链往外层逐层搜索,
一直搜索到window对象为止,找不到会抛出一个为定义的错误。而这种从内到外逐层查找的关系在js中我们称为作用域链
2. 变量的生命周期
除了变量作用域之外,另一个跟闭包有关的概念就是变量的生命周期,对于全局变量来说,全局变量的生命周期是永久的,除非我们主动消耗这个全局变量,
而对与函数内部的使用var声明的变量来说,当退出函数时,这些变量就会随这函数的结束而消耗。
3. 闭包的形成
JavaScript允许使用内部函数,可以将函数定义和函数表达式放在另一个函数的函数体内,而且,内部函数可以访问它所在的外部函数声明的局部变量、参数及声明的其他内部函数。
当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。常见的闭包写法就是简单的函数套函数,通过另一个函数访问这个函数的局部变量,
利用闭包可以突破作用域,将函数内部的变量和方法传递到外部,延续变量的生命。使用闭包可以减少环境的污染,也可以延续变量的生命。
4. 闭包的适用场景
闭包的适用场景非常广泛,首先从闭包的优点出发就是:
减少全局环境的污染生成独立的运行环境,模块化就是利用这个特点对不同的模块有自己独立的运行环境,不会和全局冲突,
模块和模块之间通过抛出接口进行依赖使用
以前像我们常用的jquery类库(避免和全局冲突使用闭包实现自己独立的环境)
可以利用这个功能做一些值的缓存工作,例如常见的设计模式(单利模式),以及现在比较火的框架vue中的计算属性;
其实当遇到一些场景的时候都可以使用闭包
1. 维护函数内的变量安全,避免全局变量的污染。
2. 维护一个变量不被回收。
3.封装模块
5. 闭包的缺点
由于闭包会使得函数中的变量被保存在内存中,内存消耗很大。所以在闭包不用之后,将不使用的局部变量删除,使其被回收。
在IE中可能导致内存泄漏,即无法回收驻留在内存中的元素,这时候需要手动释放。
6. 内存泄漏
内存泄漏指一块被分配的内存不能使用,有不能收回,直到浏览器进程结束。
出现原因:
1. 循环引用:含有DOM对象的循环引用将导致大部分当前主浏览器内存泄漏。
循环引用,简单来说假如a引用b,b又引用了a,a和b就构成了循环引用。
2. JS闭包:闭包,函数返回了内部函数还可以继续访问外边方法中定义的私有变量。
3. DOM泄露,当原有的DOM被移除时,子结点引用没有被移除则无法回收。
7. JavaScript垃圾回收机制
JavaScript中,如果一个对象不被引用,那么这个对象就会被GC(garbage collec-tion) 回收。
如果两个对象互相引用,而不再被第三者所引用,那么这两个互相引用的对象也会被回收。垃圾回收不是时时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性执行。
)