# JS面试题(二)
# 一、原型原型链
(1) 每个函数|构造函数都有一个原型prototype,用来存储公有的属性和方法
(2) 每个原型对象都有一个constructor属性,指向函数本身
(3) 每个实例都有__ proto __ 隐式指向所在类的原型
原型链的机制是:当成员访问的时候,首先查找自身的私有属性,如果没有会隐式查找所在模块原型上的属性和方法,再查不到会一直通过__ proto __ 向上查找,一直找到Object.prototype 为止。最终原型链指向null结束,这种机制称为原型链机制
1、下面代码运行的结果
function fun(){
this.a=0;
this.b=function(){
alert(this.a);
}
}
fun.prototype={
b:function(){
this.a=20;
alert(this.a);
},
c:function(){
this.a=30;
alert(this.a)
}
}
var my_fun=new fun();
my_fun.b();
my_fun.c();
结果:
"0" "30"
分析:
var my_fun=new fun(); => { a:0 , b: function(){ alert(this.a)}}
my_fun.b(); => 此时 this=> my_fun , 所以结果为"0"
my_fun.c(); => 此时my_fun中没有c这个属性,去原型中查找,打印结果为“30”
2、写出下面代码执行输出的结果
function C1(name) {
if (name) {
this.name = name;
}
}
function C2(name) {
this.name = name;
}
function C3(name) {
this.name = name || 'join';
}
C1.prototype.name = 'Tom';
C2.prototype.name = 'Tom';
C3.prototype.name = 'Tom';
alert((new C1().name) + (new C2().name) + (new C3().name));
结果:
"Tomundefinedjoin"
分析:
考察原型原型链构造函数,函数实例化,参数不传为undefined
new C1().name => 因为name为undefined,所以当前函数内没有name属性,只能去原型对象去找所以结果为"Tom"
new C2().name => 因为name为undefined,所以当前函数内拥有私有属性name为 undefined,结果为undefined
new C3().name => 因为name为undefined,所以当前函数内拥有私有属性name为 "join",结果为'join'
综上,结果为 alert("Tom" + undefined + 'join') => "Tomundefinedjoin"
3、下面代码运行的结果?
function Fn() {
let a = 1;
this.a = a;
}
Fn.prototype.say = function () {
this.a = 2;
}
Fn.prototype = new Fn;
let f1 = new Fn;
Fn.prototype.b = function () {
this.a = 3;
};
console.log(f1.a);
console.log(f1.prototype); // ?
console.log(f1.b);
console.log(f1.hasOwnProperty('b'));
console.log('b' in f1);
console.log(f1.constructor == Fn);
结果:
分析:
f1 => {a: 1 , __ proto __ : {
a =1,
b:function(){ this.a = 3;},
__ proto __ :
say: func(){this.a = 2;}
}
console.log(f1.a); => 1
console.log(f1.prototype); => {a:1,b:function(){this.a},__ proto __: {say:func(){...}} }
console.log(f1.b); => function(){ this.a =3 }
console.log(f1.hasOwnProperty('b')); => false
console.log('b' in f1); => true
console.log(f1.constructor == Fn); => true
4、写出下面代码执行输出的结果
function Foo() {
getName = function () {
console.log(1);
};
return this;
}
Foo.getName = function () {
console.log(2);
};
Foo.prototype.getName = function () {
console.log(3);
};
var getName = function () {
console.log(4);
};
function getName() {
console.log(5);
}
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
结果为:
2 4 1 1 2 3 3 Foo.getName {}
分析:
考察原型链、变量提升
词法解析阶段,var getName 和 函数getName变量提升
1、
2、getName();
3、Foo().getName();
4、getName();
5、new Foo.getName();
6、new Foo().getName();
7、new new Foo().getName();
5、完成如下的需求
let n = 10;
let m = n.plus(10).minus(5);
console.log(m);
//=>15(10+10-5)
分析:
给数字Number这个类添加原型方法
Number.prototype.plus = function(n){return this+n}
Number.prototype.minus = function(n){return this-n}
6、实现如下需求
/*
* 编写queryURLParams方法实现如下的效果(至少两种方案)
*/
let url="http://www.zhufengpeixun.cn/?lx=1&from=wx#video";
console.log(url.queryURLParams("from")); //=>"wx"
console.log(url.queryURLParams("_HASH")); //=>"video"
分析:
String.prototype. queryURLParams=function(params){ if(params==='_HASH'){ return this.slice(this.match(/#/)[1]) }else{ return this.slice(this.match(/params/)[1]) } }
7、基于ES6中的class重构下面的代码
function Modal(x,y){
this.x=x;
this.y=y;
}
Modal.prototype.z=10;
Modal.prototype.getX=function(){
console.log(this.x);
}
Modal.prototype.getY=function(){
console.log(this.y);
}
Modal.n=200;
Modal.setNumber=function(n){
this.n=n;
};
let m = new Modal(10,20);
// 重构
class Parent {
constructor() {
this.z = 10;
this.getX = function () {
console.log(x);
};
this.getY = function () {
console.log(y);
};
}
}
class Modal extends Parent{
constructor(x, y) {
super();
this.x = x;
this.y = y;
}
static n = 200;
static setNumber = function (n) {
this.n = n;
};
}
let m = new Modal(10, 20);
console.log(m);
8、代码输出的结果
let obj = {
2: 3,
3: 4,
length: 2,
push: Array.prototype.push
}
obj.push(1);
obj.push(2);
console.log(obj);
分析: ?
obj ={ 2: 3, 3: 4, length: 4, push: Array.prototype.push }
9、a等于什么值会让下面条件成立
var a = ?;
if (a == 1 && a == 2 && a == 3) {
console.log('OK');
}
分析: ?
var a = Object.defineProperty({}, 'a',{ value: ++a || 1 }).a
10、实现如下需求
let utils = (function(){
/*
* toArray:转换为数组的方法
* @params
* 不固定数量,不固定类型
* @return
* [Array] 返回的处理后的新数组
* by zhufengpeixun on 2020
*/
function toArray(){
//=>实现你的代码(多种办法实现)
}
return {
toArray
};
})();
let ary = utils.toArray(10,20,30); //=>[10,20,30]
ary = utils.toArray('A',10,20,30); //=>['A',10,20,30]
分析:
11、对象(数组)的深克隆和浅克隆(头条)
//=>浅克隆:只复制对象或者数组的第一级内容
//=>深克隆:克隆后数组的每一级都和原始数组没有关联
//那么请说出,浅克隆都怎么去实现,如何实现深度克隆
let obj = {
a: 100,
b: [10, 20, 30],
c: {
x: 10
},
d: /^\d+$/
};
let arr = [10, [100, 200], {
x: 10,
y: 20
}];
分析:
12、已知基于 instanceof 可以实现检测:实例是否属于某个类,现在需要自己编写这样的一个方法,实现出 instanceof 的效果
//=>example:要检测的实例
//=>classFunc:要检测的类
function instance_of(example, classFunc) {
//...
}
let res = instance_of([12,23],Array);
console.log(res); //=>true
分析:
# 二、附加题(偏难)
1、实现如下需求
//=>编写toType方法,实现数据类型检测
function toType( obj ) {
//完成你的代码
}
console.log(toType(1)); //=>"number"
console.log(toType(NaN)); //=>"number"
console.log(toType([])); //=>"array"
console.log(toType(/^\d+$/)); //=>"regexp"
console.log(toType({})); //=>"object"
...
2、 完成如下需求
~function(){
function change(){
//=>实现你的代码
};
Function.prototype.change=change;
}();
let obj = {name:'zhufeng'};
function func(x,y){
this.total=x+y;
return this;
}
let res = func.change(obj,100,200);
//res => {name:'Alibaba',total:300}
分析
3、完成如下需求
~function(){
//=>bind方法在IE6~8中不兼容,接下来我们自己基于原生JS实现这个方法
function bind(){
};
Function.prototype.bind=bind;
}();
var obj = {name:'zhufeng'};
function func(){
console.log(this,arguments);
//=>当点击BODY的时候,执行func方法,输出:obj [100,200,MouseEvent事件对象]
}
document.body.onclick = func.bind(obj,100,200);
结果 :
4、下面代码的输出结果?为什么?
var name = '珠峰培训';
function A(x,y){
var res=x+y;
console.log(res,this.name);
}
function B(x,y){
var res=x-y;
console.log(res,this.name);
}
B.call(A,40,30);
B.call.call.call(A,20,10);
Function.prototype.call(A,60,50);
Function.prototype.call.call.call(A,80,70);