绝大部分的函数(少数内建函数除外)都有一个
prototype
属性,这个属性是原型对象用来创建新对象实例,而所有被创建的对象都会共享原型对象,因此这些对象便可以访问原型对象的属性
js常被描述为一个基于原型的语言--每个对象拥有一个原型对象
当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次向上搜索,直到找到一个名字匹配的属性或者达到原型链的顶端
原型链
原因是每个对象都有 __proto__
属性,此属性指向该对象的构造函数的原型
原型对象也可能拥有原型,并从中继承方法和属性,一层一层,以此类推,这种关系被称为原型链,他解释了为何一个对象会拥有定义在其他对象中的属性和方法
在对象实例和他的构造器之间建立一个链接,_proto_属性,是从构造函数的prototype属性中派生的,之后可以通过上溯原型链,在构造器中找到这些属性和方法
总结:
一起对象都是继承自object对象,object对象直接继承根源对象null
一切的函数对象,都是继承自Function对象
object对象直接继承自Function对象
Function对象的_proto_会指向自己的原型对象,最总还是继承自object对象
3.说一下JS继承(含ES6的)
JS
继承实现方式也很多,主要分
ES5
和
ES6
继承的实现
继承的优点:
继承可以使得子类具有父类别的各种属性和方法,而不需要再次编写相同的代码
再子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得于父类别不同的功能
先说一下
ES5
是如何实现继承的
ES5
实现继承主要是基于
prototype
来实现的,具体有三种方法
一是
原型链继承
:即
B.prototype=new A()
二是借
用构造函数继承
(call
或者
apply
的方式继承
)
三
是组合继承
实现方式:
原型链继承:是比较常见的一种继承方式之一,其中设计的构造函数、原型和实例,三者之间存在一定的关系,即每一个构造函数中都会有一个原型对象,原型对象有包含一个指向构造函数的指针,而实例则包含一个原型对象的指针
构造函数继承(借助 call)借助call调用Parent函数,父类原型对象中一旦存在父类之前自己定义的方法,那么子类将无法继承这些方法,父类的引用属性不会被共享,优化了原型链继承的弊端,但是它只能继承父类的实例属性和方法,不能继承原型属性或者方法
组合继承:组合继承就是将原型链继承和构造函数继承结合起来
原型式继承:借助Object.create方法实现普通对象的继承:因为它实现的是浅拷贝,多个实例的引用类型属性指向相同的内存,存在篡改的可能
寄生式继承:就是利用浅拷贝的能力进行增强,添加一些其他方法
寄生组合式继承:借助解决普通对象的继承问题的Object.create 方法,在前面几种继承方式的优缺点基础上进行改造,这也是所有继承方式里面相对最优的继承方式
4.说一下JS原生事件如何绑定
JS
原生绑定事件主要为三种:
一是
html
事件处理程序
二是
DOM0
级事件处理程序
三是
DOM2
级事件处理程序
html
事件现在早已不用了,就是在
html
各种标签上直接添加事件,类似于
css
的行内样
式,缺点是不好维护,因为散落在标签中
,
也就是耦合度太高
javascript中的事件,可以理解就是在HTML文档或者浏览器中发生的一种交互操作,使得网页具备互动性, 常见的有加载事件、鼠标事件、自定义事件等
事件流都会经历三个阶段:
事件捕获阶段(capture phase)
处于目标阶段(target phase)
事件冒泡阶段(bubbling phase)
事件冒泡是一种从下往上的传播方式,由最具体的元素(触发节点)然后逐渐向上传播到最不具体的那个节点,也就是DOM中最高层的父节点
事件捕获与事件冒泡相反,事件最开始由不太具体的节点最早接受事件, 而最具体的节点(触发节点)最后接受事件
事件模型可以分为三种:
原始事件模型(DOM0级)html代码直接绑定,js代码绑定
特性:
绑定速度快
只支持冒泡,不支持捕获
同一个类型的事件只能绑定一次
标准事件模型(DOM2级)
事件捕获阶段:事件从document一直向下传播到目标元素, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行
事件处理阶段:事件到达目标元素, 触发目标元素的监听函数
事件冒泡阶段:事件从目标元素冒泡到document, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行
特性:
可以在一个DOM元素上绑定多个事件处理器,各自并不会冲突
当第三个参数(useCapture)设置为true就在捕获过程中执行,反之在冒泡过程中执行处理函数
IE事件模型(基本不用
IE事件模型共有两个过程:
事件处理阶段:事件到达目标元素, 触发目标元素的监听函数。
事件冒泡阶段:事件从目标元素冒泡到document, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行
5.说一下JS原生常用dom操作方法?
查找:
getElementByid,
getElementsByTagName,
querySelector,
querySelectorAll
插入:
appendChild,insertBefore
删除:
removeChild
克隆:
cloneNode
设置和获取属性:
setAttribute(“
属性名
”,”
值
”)
getAttibute(“
属性名
”)
6.说一下ES6新增特性?
1.
新增了块级作用域
(let,const)
2.
提供了定义类的语法糖
(class)
3.
新增了一种基本数据类型
(Symbol)
4.
新增了变量的解构赋值
5.
函数参数允许设置默认值,引入了
rest
参数,新增了箭头函数
6.
数组新增了一些
API
,如
isArray / from / of
方法
;
数组实例新增了
entries()
,
keys()
和
values()
等
方法
7.
对象和数组新增了扩展运算符
8. ES6
新增了模块化
(import/export)
9. ES6
新增了
Set
和
Map
数据结构
10. ES6
原生提供
Proxy
构造函数,用来生成
Proxy
实例
11. ES6
新增了生成器
(Generator)
和遍历器
(Iterator)
7.JS设计模式有哪些(单例模式观察者模式等)
JS
设计模式有很多,但我知道的有单例模式,观察者模式
单例模式:
就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果
不存在就创建了再返回,这就确保了一个类只有一个实例对象。在
JavaScript
里,单例作为一个命
名空间提供者,从全局命名空间里提供一个唯一的访问点来访问该对象。
观察者模式
:
观察者的使用场合就是:当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象
需要改变的时候,就应该考虑使用观察者模式。
总的来说,观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。从而 使得各自的变化都不会影响到另一边的变化
8.说一下你对JS面试对象的理解 ?
JS
面向对象主要基于
function
来实现的,通过
function
来模拟类,通过
prototype
来实现类方法的共享, 跟其他语言有着本质的不同,自从有了ES6
后,把面向对象类的实现更像后端语言的实现了,通过
class 来定义类,通过extends
来继承父类,其实
ES6
类的实现本质上是一个语法糖,不过对于开发简单了好多
9.说一下JS数组常用方法(至少6个)
1. Array.push(),向数组的末尾添加一个或多个元素,并返回新的数组长度。原数组改变
2. Array.pop(),删除并返回数组的最后一个元素,若该数组为空,则返回undefined。原数组改变
3. Array.unshift(),向数组的开头添加一个或多个元素,并返回新的数组长度。原数组改变。
4. Array.shift(),删除数组的第一项,并返回第一个元素的值。若该数组为空,则返回undefined。原数组改变。
5. Array.concat(arr1,arr2…),合并两个或多个数组,生成一个新的数组。原数组不变。
6. Array.join(),将数组的每一项用指定字符连接形成一个字符串。默认连接字符为 “,” 逗号。
7. Array.reverse(),将数组倒序。原数组改变。
8. Array.sort(),对数组元素进行排序。按照字符串UniCode码排序,原数组改变。
9.Array.map(function),原数组的每一项执行函数后,返回一个新的数组。原数组不变。(注意该方法和forEach的区别)。
10.Array.slice() 按照条件查找出其中的部分内容
11.Array.splice(index,howmany,arr1,arr2…) ,用于添加或删除数组中的元素。从index位置开始删除howmany个元素,并将arr1、arr2…数据从index位置依次插入。howmany为0时,则不删除元素。原数组改变。
12.Array.forEach(function),用于调用数组的每个元素,并将元素传递给回调函数。原数组不变。(注意该方法和map的区别,若直接打印Array.forEach,结果为undefined)。
13.Array.filter(function),过滤数组中,符合条件的元素并返回一个新的数组
14.Array.every(function),对数组中的每一项进行判断,若都符合则返回true,否则返回false。
15.Array.some(function),对数组中的每一项进行判断,若都不符合则返回false,否则返回true。
16.Array.reduce(function),reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
17.indexOf() 检测当前值在数组中第一次出现的位置索引
原数组改变的方法有:push pop shift unshift reverse sort splice forEach
不改变原数组的方法有:concat map filter join every some indexOf slice
10.说一下JS数组内置遍历方法有哪些和区别 ?
forEach
这个方法是为了取代
for
循环遍历数组的,返回值为
undefined
item
代码遍历的每一项
,
index:
代表遍历的每项的索引,
arr
代表数组本身
filter