刷前端面经笔记(八)
1.apply,call,bind有什么区别?
三者都可以把一个函数应用到其他对象上,apply
,call
是直接执行函数调用,bind
是绑定,执行需要再次调用。
apply
和call
的区别是apply
接受数组作为参数,而call
是接受逗号分隔的无限多个参数列表。
代码如下:
function Person() {
}
Person.prototype.sayName() { alert(this.name); }
var obj = {name: 'michaelqin'}; // 注意这是一个普通对象,它不是Person的实例
// 1) apply
Person.prototype.sayName.apply(obj, [param1, param2, param3]);
// 2) call
Person.prototype.sayName.call(obj, param1, param2, param3);
// 3) bind
var liaoke = Person.prototype.sayName.bind(obj);
liaoke ([param1, param2, param3]); // bind需要先绑定,再执行
liaoke (param1, param2, param3); // bind需要先绑定,再执行
2.介绍一下defineProperty,hasOwnProperty, propertyIsEnumerable
Object.defineProperty(obj,prop,descriptor)
用来给对象定义属性,有value
,writeable
,enumerable
,set/get
,configurable
,
hasOwnProperty
用于检查某一属性是不是存在于对象本身,
propertyIsEnumerable
用来检测某一属性是否可遍历,也就是能不能用for...in
循环来取到。
3.JS常用设计模式的实现思路(单例、工厂、代理、装饰、观察者模式等)
// 1) 单例: 任意对象都是单例,无须特别处理
var obj = {name: 'michaelqin', age: 30};
// 2) 工厂: 就是同样形式参数返回不同的实例
function Person() { this.name = 'Person1'; }
function Animal() { this.name = 'Animal1'; }
function Factory() {}
Factory.prototype.getInstance = function(className) {
return eval('new ' + className + '()');
}
var factory = new Factory();
var obj1 = factory.getInstance('Person');
var obj2 = factory.getInstance('Animal');
console.log(obj1.name); // Person1
console.log(obj2.name); // Animal1
// 3) 代理: 就是新建个类调用老类的接口,包一下
function Person() { }
Person.prototype.sayName = function() { console.log('michaelqin'); }
Person.prototype.sayAge = function() { console.log(30); }
function PersonProxy() {
this.person = new Person();
var that = this;
this.callMethod = function(functionName) {
console.log('before proxy:', functionName);
that.person[functionName](); // 代理
console.log('after proxy:', functionName);
}
}
var pp = new PersonProxy();
pp.callMethod('sayName'); // 代理调用Person的方法sayName()
pp.callMethod('sayAge'); // 代理调用Person的方法sayAge()
// 4) 观察者: 就是事件模式,比如按钮的onclick这样的应用.
function Publisher() {
this.listeners = [];
}
Publisher.prototype = {
'addListener': function(listener) {
this.listeners.push(listener);
},
'removeListener': function(listener) {
delete this.listeners[listener];
},
'notify': function(obj) {
for(var i = 0; i < this.listeners.length; i++) {
var listener = this.listeners[i];
if (typeof listener !== 'undefined') {
listener.process(obj);
}
}
}
}; // 发布者
function Subscriber() {
}
Subscriber.prototype = {
'process': function(obj) {
console.log(obj);
}
}; // 订阅者
var publisher = new Publisher();
publisher.addListener(new Subscriber());
publisher.addListener(new Subscriber());
publisher.notify({name: 'michaelqin', ageo: 30}); // 发布一个对象到所有订阅者
publisher.notify('2 subscribers will both perform process'); // 发布一个字符串到所有订阅者
3.处理字符串常用的十个函数
charAt() // 返回在指定位置的字符。
concat() // 连接字符串。
fromCharCode() // 从字符编码创建一个字符串。
indexOf() // 检索字符串。
match() // 找到一个或多个正则表达式的匹配。
replace() // 替换与正则表达式匹配的子串。
search() // 检索与正则表达式相匹配的值。
slice() // 提取字符串的片断,并在新的字符串中返回被提取的部分。
split() // 把字符串分割为字符串数组。
substr() // 从起始索引号提取字符串中指定数目的字符。
substring() // 提取字符串中两个指定的索引号之间的字符。
toLocaleLowerCase() // 把字符串转换为小写。
toLocaleUpperCase() // 把字符串转换为大写。
toLowerCase() // 把字符串转换为小写。
toUpperCase() // 把字符串转换为大写。
toString() // 返回字符串。
valueOf() // 返回某个字符串对象的原始值。
4.如何判断一个变量是对象还是数组
function isObjArr(variable){
if (Object.prototype.toString.call(value) === "[object Array]") {
console.log('value是数组');
}else if(Object.prototype.toString.call(value)==='[object Object]'){//这个方法兼容性好一点
console.log('value是对象');
}else{
console.log('value不是数组也不是对象')
}
}
// 注意:千万不能使用typeof来判断对象和数组,因为这两种类型都会返回"object"。
5.ES5的继承和ES6的继承有什么区别?
ES5
的继承是通过prototype
或构造函数机制来实现。
ES5
的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this
上(Parent.apply(this)
)。
ES6
的继承机制实质上是先创建父类的实例对象this
(所以必须先调用父类的super()
方法),然后再用子类的构造函数修改this
。具体为ES6
通过class
关键字定义类,里面有构造方法,类之间通过extends
关键字实现继承。子类必须在constructor
方法中调用super
方法,否则新建实例报错。因为子类没有自己的this
对象,而是继承了父类的this
对象,然后对其调用。如果不调用super
方法,子类得不到this
对象。
注意:super关键字指代父类的实例,即父类的this对象。在子类构造函数中,调用super后,才可使用this关键字,否则报错。
6.下面的ul,如何点击每一列的时候alert其index?(闭包)
<ul id="test">
<li>这是第一条</li>
<li>这是第二条</li>
<li>这是第三条</li>
</ul>
// 方法一:
var lis=document.getElementById('test').getElementsByTagName('li');
for(var i=0;i<3;i++)
{
lis[i].index=i;
lis[i].onclick=function(){
alert(this.index);
};
}
//方法二:
var lis=document.getElementById('test').getElementsByTagName('li');
for(var i=0;i<3;i++)
{
lis[i].index=i;
lis[i].onclick=(function(a){
return function() {
alert(a);
}
})(i);
}
7.对于MVVM的理解
MVVM
是 Model-View-ViewModel
的缩写。
Model
代表数据模型,也可以在Model
中定义数据修改和操作的业务逻辑。
View
代表UI
组件,它负责将数据模型转化成UI
展现出来。
ViewModel
监听模型数据的改变和控制视图行为、处理用户交互,
简单理解就是一个同步View
和 Model
的对象,连接Model
和View
。
在MVVM
架构下,View
和 Model
之间并没有直接的联系,
而是通过ViewModel
进行交互,Model
和 ViewModel
之间的交互是双向的,
因此View
数据的变化会同步到Model
中,而Model
数据的变化也会立即反应到View
上。
ViewModel
通过双向数据绑定把 View
层和 Model
层连接了起来,
而View
和 Model
之间的同步工作完全是自动的,无需人为干涉,
因此开发者只需关注业务逻辑,不需要手动操作DOM
,
不需要关注数据状态的同步问题,
复杂的数据状态维护完全由 MVVM
来统一管理。
8.解释Vue的生命周期
Vue
实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载Dom->渲染、更新->渲染、销毁等一系列过程,称之为Vue
的生命周期。
Vue
的生命周期包括:
beforeCreate
(创建前)在数据观测和初始化事件还未开始,
created
(创建后)完成数据观测,属性和方法的运算,初始化事件,$el
属性还没有显示出来;
beforeMount(
载入前)在挂载开始之前被调用,相关的render
函数首次被调用,实例已完成以下的配置:编译模板,把data
里面的数据和模板生成html
,注意此时还没有挂载html
到页面上;
mounted
(载入后)在el
被新创建的vm.$el
替换,并挂载到实例上去之后调用,实例已完成以下配置:用上面编译好的html
内容替换el
属性指向的DOM
对象,完成模板中的html
渲染到html
页面中,此过程中进行ajax
交互。
beforeUpdate
(更新前)在数据更新之前调用,发生在虚拟DOM
重新渲染和打补丁之前,可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。
updated
(更新后)在由于数据更改导致的虚拟DOM
重新渲染和打补丁之后调用。调用时,组件DOM
已经更新,所以可以执行依赖于DOM
的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环,该钩子在服务器渲染期间不被调用。
beforeDestroy
(销毁前)在实例销毁之前调用,实例仍然完全可用。
destroyed
(销毁后)在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
9.为什么使用Node.js,它有哪些优缺点?
优点:
事件驱动,通过闭包很容易实现客户端的生命活期。
不用担心多线程,锁,并行计算的问题
V8
引擎速度非常快
对于游戏来说,写一遍游戏逻辑代码,前端后端通用
缺点:
node.js
更新很快,可能会出现版本兼容
node.js
还不算成熟,还没有大制作
node.js
不像其他的服务器,对于不同的链接,不支持进程和线程操作
10.什么是错误优先的回调函数?
错误优先(Error-first
)的回调函数(Error-First Callback
)用于同时返回错误和数据。
第一个参数返回错误,并且验证它是否出错;其他参数返回数据。
fs.readFile(filePath, function(err, data)
{
if (err)
{
// 处理错误
return console.log(err);
}
console.log(data);
});
11.使用npm有哪些好处?
通过npm
,你可以安装和管理项目的依赖,
并且能够指明依赖项的具体版本号。
对于Node
应用开发而言,
可以通过package.json
文件来管理项目信息,
配置脚本,以及指明依赖的具体版本。
12.在JavaScript源文件的开头包含 use strict 有什么意义和好处?
use strict
是一种在JavaScript
代码运行时自动实行更严格解析和错误处理的方法。(严格模式)
将值分配给一个未声明的变量会自动创建该名称的全局变量。这是JavaScript
中最常见的错误之一。在严格模式下,这样做的话会抛出错误。消除 this
强制。
当检测到对象(例如,var object = {foo: "bar", foo: "baz"};
)中重复命名的属性,或检测到函数中(例如,function foo(val1, val2, val1){}
)重复命名的参数时,严格模式会抛出错误,因此捕捉几乎可以肯定是代码中的bug
可以避免浪费大量的跟踪时间。比eval()
更安全。
13.vuejs与angularjs以及react的区别?
与AngularJS
的区别
相同点:
都支持指令:内置指令和自定义指令。
都支持过滤器:内置过滤器和自定义过滤器。
都支持双向数据绑定。
都不支持低端浏览器。
不同点:
1.AngularJS
的学习成本高,比如增加了Dependency Injection
特性,而Vue.js
本身提供的API
都比较简单、直观。
2.在性能上,AngularJS
依赖对数据做脏检查,所以Watcher
越多越慢。
Vue.js
使用基于依赖追踪的观察并且使用异步队列更新。所有的数据都是独立触发的。
对于庞大的应用来说,这个优化差异还是比较明显的。
与React
的区别
相同点:
React
采用特殊的JSX
语法,Vue.js
在组件开发中也推崇编写.vue
特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用。
中心思想相同:一切都是组件,组件实例之间可以嵌套。
都提供合理的钩子函数,可以让开发者定制化地去处理需求。
都不内置列数AJAX
,Route
等功能到核心包,而是以插件的方式加载。
在组件开发中都支持mixins
的特性。
不同点:
React
依赖Virtual DOM
,而Vue.js
使用的是DOM
模板。React
采用的Virtual DOM
会对渲染出来的结果做脏检查。
Vue.js
在模板中提供了指令,过滤器等,可以非常方便,快捷地操作Virtual DOM
。
14.标签keep-alive的作用是什么?
<keep-alive></keep-alive>
包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。
15.WeakMap 和 Map 的区别?
WeakMap
结构与 Map
结构基本类似,唯一的区别是它只接受对象作为键名(null
除外),不接受其他类型的值作为键名,而且键名所指向的对象,不计入垃圾回收机制。
WeakMap
最大的好处是可以避免内存泄漏。一个仅被 WeakMap
作为key
而引用的对象,会被垃圾回收器回收掉。
WeakMap
拥有和Map
类似的 set(key, value)
、get(key)
、has(key)
、delete(key)
和 clear()
方法, 没有任何与迭代有关的属性和方法。
16.http和https的基本概念?
http
: 超文本传输协议,是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP
),用于从WWW
服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。
https
: 是以安全为目标的HTTP
通道,简单讲是HTTP
的安全版,即HTTP
下加入SSL
层,HTTPS
的安全基础是SSL
,因此加密的详细内容就需要SSL
。
https
协议的主要作用是:建立一个信息安全通道,来确保数组的传输,确保网站的真实性。
17.git fetch和git pull的区别?
git pull
:相当于是从远程获取最新版本并merge
到本地
git fetch
:相当于是从远程获取最新版本到本地,不会自动merge
18.介绍一下对浏览器内核的理解?
主要分成两部分:渲染引擎(layout engineer
或Rendering Engine
)和JS
引擎。
渲染引擎:负责取得网页的内容(HTML
、XML
、图像等等)、
整理讯息(例如加入CSS
等),以及计算网页的显示方式,然后会输出至显示器或打印机。
浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。
所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。
JS
引擎:解析和执行javascript
来实现网页的动态效果。
最开始渲染引擎和JS
引擎并没有区分的很明确,后来JS
引擎越来越独立,内核就倾向于只指渲染引擎。
19.什么是微格式
微格式(Microformats
)是一种让机器可读的语义化XHTML
词汇的集合,是结构化数据的开放标准。
是为特殊应用而制定的特殊格式
优点:将智能数据添加到网页上,让网站内容在搜索引擎结果界面可以显示额外的提示。
20.数据绑定基本的实现
// 实现一个方法,可以给 obj 所有的属性添加动态绑定事件,当属性值发生变化时会触发事件
let obj = {
key_1: 1,
key_2: 2
}
function func(key) {
console.log(key + ' 的值发生改变:' + this[key]);
}
bindData(obj, func);
obj.key_1 = 2; // 此时自动输出 "key_1 的值发生改变:2"
obj.key_2 = 1; // 此时自动输出 "key_2 的值发生改变:1"
答案:
function bindData(obj, fn) {
for (let key in obj) {
Object.defineProperty(obj, key, {
set(newVal) {
if (this.value !== newVal) {
this.value = newVal;
fn.call(obj, key);
}
},
get() {
return this.value;
}
})
}
}
20.数据结构处理
// 有一个祖先树状 json 对象,当一个人有一个儿子的时候,其 child 为其儿子对象,如果有多个儿子,child 为儿子对象的数组。
请实现一个函数,找出这个家族中所有有多个儿子的人的名字(name),输出一个数组。
列子:
// 样例数据
let data = {
name: 'jack',
child: [
{ name: 'jack1' },
{
name: 'jack2',
child: [{
name: 'jack2-1',
child: { name: 'jack2-1-1' }
}, {
name: 'jack2-2'
}]
},
{
name: 'jack3',
child: { name: 'jack3-1' }
}
]
}
// 答案:
// 用递归
function findMultiChildPerson(data) {
let nameList = [];
function tmp(data) {
if (data.hasOwnProperty('child')) {
if (Array.isArray(data.child)) {
nameList.push(data.name);
data.child.forEach(child => tmp(child));
} else {
tmp(data.child);
}
}
}
tmp(data);
return nameList;
}
// 不用递归
function findMultiChildPerson(data) {
let list = [data];
let nameList = [];
while (list.length > 0) {
const obj = list.shift();
if (obj.hasOwnProperty('child')) {
if (Array.isArray(obj.child)) {
nameList.push(obj.name);
list = list.concat(obj.child);
} else {
list.push(obj.child);
}
}
}
return nameList;
}
![](https://img.haomeiwen.com/i6100502/c094f374437d9b4e.jpg)