关于JavaScript的面试(一)

  • 白小霁
  • 12 Minutes
  • June 15, 2017

关于this的东西

this是执行上下的最好体现,其实这里不会讲具体的东西,只是一点点的回顾。在整个JavaScript中,有哪些API或关键字是会和this的指向和绑定有问题的。来我们理一下:

  1. call / apply :其实这两个API是确定this的东西,也是可以指定this的API,第一个参数就是this的值。这两个区别的主要在于第二个参数的区别,call的是以一个一个的参数传递的,而apply的第二个参数就是以数组的方式传递参数。
  2. bind:这个API就是也是第一个参数绑定函数的this值,返回一个新的函数再次调用。其实这个函数还有一个特性就是this指向的值不能修改的,因为返回的新的函数,这个函数里面是执行了原本的函数。所以有这么一个问题看链接,最后的call方法只是改变了包裹函数的this值却没有改变bind的函数最终返回的结果。这个真的要看英文文档和源码才能知道的一点:
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
// http://ejohn.org/apps/learn/ The .bind method from Prototype.js
Function.prototype.bind = function(){
var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
return function(){
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
// polyfill状态
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP
? this
: oThis || this,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
  1. new:其实很多情况下,会忘记这个关键字的存在,因为觉得理所当然了,而这个关键字的作用就是能将函数的this指向实例化出来的对象
  2. 特殊情况下,在ES6 语法中箭头函数也是绑定了this,其指向的就是最外层的词法作用域,在这里就不展开这个知识点。

有关于继承的知识点

今早的有一个面试,问道我对OOP编程的看法。哈哈,其实还是一个编程的思潮而已,可谈到相面对象的主要会引导一个问题就是继承。而继承这个东西在JavaScript中,并没有像Java的当中那样extends,可有些人了解到了ES6的东西,里面确实有这个语法糖可以继承一些组件。但涉及到原生的JavaScript的时候,这一点的引出的原型链这个知识点最为重要。

那我们来想一个需求,我手上有一个human的Fn,看下面:

1
2
3
4
5
6
7
function human(name, age){
this.name = name
this.age = age
}
human.prototype.sayName = function(){
console.log(this.name)
}

那我现在想要创建一个student的Fn,去继承human的类,就有了如下的:

1
2
3
4
5
...
function student(name,age, college){
human.call(this,name,age)
this.college = college
}

其实以上是可以的了,但是有一个问题呀!我们这时候不能调用human.sayName,这时候我们就要想到了原型链的东西,其实想想也知道很多的公共函数都是放在对象的原型链上的,那我们就将student的原型指向humane的原型不就好了吗?所以我们这样做:

1
student.prototype = human.prototype

其实这样大致也是可以的,只不过这里出现了一个问题,我想给student的原型链上的加上一些东西,其实也就是加在了human的原型上了,这样使得human函数变得不是那么pure,对于之后使用human的拓展不是很好,所以我们可以使用新的函数来指向human的原型,于是我们这样做:

1
2
3
var Fn = function (){}
Fn.prototype = human.prototype
student.prototype = new Fn()

这样我们可以在原型链上使用了human.sayName方法,而我们在student原型上加方法也不会改到了human的方法。其实这样的方法也是很复杂的,想想整体的思路,就是指向human的对象吧,那我们想想能不能拷贝一份这样的对象,噢!想到了,Object.create()看这里,所以我们改写了代码:

1
student.prototype = Object.create(human.prototype)

以上就是笔者对继承的了解。

排序算法

名词解释:

n:数据规模

k:“桶”的个数

In-place:占用常数内存,不占用额外内存

Out-place:占用额外内存

稳定性:排序后2个相等键值的顺序和排序之前它们的顺序相同

冒泡排序:「教官双手算法:较高的往后排」

两个循环体完成整个排序,第一个循环体主要引导循环的次数,第二个循环体的作用是当前循环中的大小比较。说白了整个逻辑是都要循环,次数是一次比一次少一个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var arr = [4,5,1,2,3,7,6]
function bubbleSort(arr) {
var len = arr.length
for(var i = 0; i<len; i++){
for(var j = 0; j<len-1-i; j++){
if(arr[j]>arr[j+1]){
swap(arr,j+1,j)
}
}
}
return arr
}
function swap(arr,i,j){
var temp = arr[i]
arr[i] = arr[j]
arr[j] = temp;
}