刷前端面经笔记(八)

28次阅读

共计 8767 个字符,预计需要花费 22 分钟才能阅读完成。

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,isEnumerable
Object.defineProperty(obj,prop,descriptor)用来给对象定义属性,有 value,writeable,enumerable,set/get,configurable,hasOwnProperty 用于检查某一属性是不是存在于对象本身,isEnumerable 用来检测某一属性是否可遍历,也就是能不能用 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;
}

正文完
 0