阿里第一次电面试题记录

Author Avatar
AppleSun 7月 25, 2018

题目已整理好 答案还在完善中。。。

Q: 1. 简历上说 你做过 图片上传预览的封装 说说怎么实现的吧?

A:

针对 input[type='file'] 的 accept 属性接收图片,通过input 的 change事件 获得 file对象。

校验 图片的文件后缀(file.type) 以及 文件尺寸大小(file.size)之后,实例化一个 FileReader,针对 FileReader 的 onload事件,使用FileReader对象的readAsDataURL方法来读取图像文件。

预览功能 则借助 swiper 进行 手势缩放拖拽 等实现

Q: 1-1. 预览图片过程中,你有没有遇到 图片 歪了 的情况,你怎么解决的 为什么会有这情况,什么时候会有这情况

A:

歪了是因为 图片信息中携带的 Orientation信息。在 FileReader获得图片之前, 针对此 需要 通过引入 EXIF.js,获取 角度信息,然后通过 canvas 旋转对应图片信息 生成新的图片 继续上面的过程。

Q: 2. webpack 和 gulp 有什么区别

A:

Gulp 的定位是 Task Runner, 就是用来跑一个一个任务的。 放在以前比如我想用sass写css, coffee写js, 我必须手动的用相应的compiler去编译各自的文件,然后各自minify。但是它没发解决的是 js module 的问题,是你写代码时候如何组织代码结构的问题。

把所有的文件(css, image, js) 都用 js 来 生成依赖,最后生成一个bundle呢? 所以webpack 也叫做file bundler。

gulp is a toolkit for automating painful or time-consuming tasks in your development workflow, so you can stop messing around and build something.

webpack is a module bundler. It packs CommonJs/AMD modules i. e. for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand.

Q: 2-1. 说说 webpack 的loader 的工作原理

A:

Loader 可以理解为是模块和资源的转换器,它本身是一个函数,接受源文件作为参数,返回转换的结果,例如可以使用loader加载器可以快速编译预处理器(less,sass,coffeeScript)。 Loader 可以在require()引用模块的时候添加,也可以在 webpack 全局配置中进行绑定,还可以通过命令行的方式使用。

特征:

  • loaders可以串联,他们应用于管道资源,最后的loader将返回javascript,其它的可返回任意格式(传递给下一个loader)
  • loaders 可以同步也可以异步
  • loaders在nodejs下运行并且可以做一切可能的事 loader接受参数,可用于配置里
  • loaders可以绑定到extension/RegExps 配置
  • loaders可以通过npm发布和安装 正常的模块儿可以到处一个
  • loader除了 loaders可以访问配置 插件可以给loaders更多的特性
  • loaders可以释放任意额外的文件

Q: 3. jQuery 的项目 你是 如何 实现 可维护的

A:

详见 下一篇博客 高效-jQuery-开发

Q: 4. vue是怎么实现 数据响应的

A:

详见 下一篇博客 vue响应数据原理

Q: 4-1. Vue 里面 是怎么 对 数组实现数据响应的

A:
1
2
3
4
5
6
7
8
9
10
['push','pop','shift','unshift','splice','sort', reverse'].forEach(function (method) {
const original = arrayProto[method]
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args)
……
//通知变化
ob.dep.notify()
return result
})
})

(非枚举的数组修改可能会没法通过vue直接自动更新视图,vue官方文档 也给出了相关方法去处理)

①数据动态响应式是Vue的核心思想之一,动态响应可以简单理解为:数据更新→视图更新;视图更新→数据更新,这里的数据分为对象数组
②对象:Vue处理对象的方式是通过Object.defineProperty劫持数据,通过gettersetter来处理
③数组:通过劫持数组的原型方法;将所有可能使得数组产生变化的方法劫持,当数据调用这些方法的时候,dep.notify(),会通知依赖于此数据的视图`update()

Q: 4-2. 讲讲Vue.set

A:

详见新的章节 Vue.set全局操作

Q: 4-3. 那你谈谈 vueJS 的 组件通信

A:

详见 新的章节 vue组件通信

Q: 4-4. 谈谈 vue 组件化工程化的理解

A:

前端方面,随着技术的发展,开发的复杂度也越来越高,传统开发模式总是存在着开发效率低,维护成本高等的弊端。传统开发方式效率低以及维护成本高的主要原因在于很多时候是将一个系统做成了整块应用,而且往往随着业务的增长或者变更,系统的复杂度会呈现指数级的增长,经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改,造成牵一发而动全身。

针对此弊端,其实业界早就有了一些探索,我们希望一个大且复杂的场景能够被分解成几个小的部分,这些小的部分彼此之间互不干扰,可以单独开发,单独维护,而且他们之间可以随意的进行组合

前端组件化的4个原则

前面讲了组件化开发的发展过程,那么我们该怎么做组件化呢?我认为组件应该遵守以下几个原则:

  • 标准性

任何一个组件都应该遵守一套标准,可以使得不同区域的开发人员据此标准开发出一套标准统一的组件。

  • 组合性

组件之前应该是可以组合的。我们知道前端页面的展示都是一些HTML DOM的组合,而组件在最终形态上也可以理解为一个个的HTML片段。那么组成一个完整的界面展示,肯定是要依赖不同组件之间的组合,嵌套以及通信。

  • 重用性

任何一个组件应该都是一个可以独立的个体,可以使其应用在不同的场景中。

  • 可维护性

任何一个组件应该都具有一套自己的完整的稳定的功能,仅包含自身的,与其它组件无关的逻辑,使其更加的容易理解,使其更加的容易理解,同时大大减少发生bug的几率。

Q: 5. 谈谈 Promise 的 实现规范

A:

参考链接 https://juejin.im/post/5a60acce518825734d14ad98

Q: 6. 你是如何做 数据去重的

A:

请直接参考 https://juejin.im/post/5949d85f61ff4b006c0de98b

Q: 6-1. .set() 是怎么实现数组去重的

A:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
(function(global) {
var NaNSymbol = Symbol('NaN');
var encodeVal = function(value) {
return value !== value ? NaNSymbol : value;
}
var decodeVal = function(value) {
return (value === NaNSymbol) ? NaN : value;
}
var makeIterator = function(array, iterator) {
var nextIndex = 0;
// new Set(new Set()) 会调用这里
var obj = {
next: function() {
return nextIndex < array.length ? { value: iterator(array[nextIndex++]), done: false } : { value: void 0, done: true };
}
};
// [...set.keys()] 会调用这里
obj[Symbol.iterator] = function() {
return obj
}
return obj
}
function forOf(obj, cb) {
let iterable, result;
if (typeof obj[Symbol.iterator] !== "function") throw new TypeError(obj + " is not iterable");
if (typeof cb !== "function") throw new TypeError('cb must be callable');
iterable = obj[Symbol.iterator]();
result = iterable.next();
while (!result.done) {
cb(result.value);
result = iterable.next();
}
}
function Set(data) {
this._values = [];
this.size = 0;
if (data) {
forOf(data, (item) => {
this.add(item);
})
}
}
Set.prototype['add'] = function(value) {
value = encodeVal(value);
if (this._values.indexOf(value) == -1) {
this._values.push(value);
++this.size;
}
return this;
}
Set.prototype['has'] = function(value) {
return (this._values.indexOf(encodeVal(value)) !== -1);
}
Set.prototype['delete'] = function(value) {
var idx = this._values.indexOf(encodeVal(value));
if (idx == -1) return false;
this._values.splice(idx, 1);
--this.size;
return true;
}
Set.prototype['clear'] = function(value) {
this._values = [];
this.size = 0;
}
Set.prototype['forEach'] = function(callbackFn, thisArg) {
thisArg = thisArg || global;
for (var i = 0; i < this._values.length; i++) {
callbackFn.call(thisArg, this._values[i], this._values[i], this);
}
}
Set.prototype['values'] = Set.prototype['keys'] = function() {
return makeIterator(this._values, function(value) { return decodeVal(value); });
}
Set.prototype['entries'] = function() {
return makeIterator(this._values, function(value) { return [decodeVal(value), decodeVal(value)]; });
}
Set.prototype[Symbol.iterator] = function(){
return this.values();
}
Set.prototype['forEach'] = function(callbackFn, thisArg) {
thisArg = thisArg || global;
var iterator = this.entries();

forOf(iterator, (item) => {
callbackFn.call(thisArg, item[1], item[0], this);
})
}
Set.length = 0;
global.Set = Set;
})(this);

实现过程参考 https://github.com/mqyqingfeng/Blog

Q: 7. 谈谈深拷贝与浅拷贝

A:

参考链接 https://juejin.im/post/59ac1c4ef265da248e75892b

答案还在完善中。。。wait