1、编写plus和minus实现如下需求
let n = 10;
let m = n.plus(10).minus(5);
console.log(m);
此题写法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>面试题</title>
</head>
<body>
<script>
// // 往所属的内置类Number的原型上扩展plus和minus方法
// 写法1、Number.prototype.plus = function plus(num) { //函数表达式方法,将匿名函数改为具名函数,在内部可以调用,但在全局不行,有益于以后使用递归,不会与全局冲突。
// // =>this指向的是我们要操作的那个数字实例(对象)
// // 不管是否是数字,强制转换为数字类型
// num = Number(num);
// if (isNaN(num)) {
// num = 0;
// }
// // console.log(this); //Number__proto__: Number[[PrimitiveValue]]: 10 原始值是10,this是一个对象
// //new Number(10)+10 = 20是可用的
// // 返回Number的实例,实现链式写法
// return this + num;
// }
// Number.prototype.minus = function minus(num) {
// // 不管是否是数字,强制转换为数字类型
// num = Number(num);
// if (isNaN(num)) {
// num = 0;
// }
// return this - num;
// }
// //写法2、 以上代码是可行的,但是判断num的代码是相同的,所以可以提取出来
// const checkNum = function checkNum(num) {
// // 不管是否是数字,强制转换为数字类型
// num = Number(num);
// if (isNaN(num)) {
// num = 0;
// }
// return num;
// }
// // 提取后,在方法中调用
// Number.prototype.plus = function plus(num) {
// return this + checkNum(num);
// }
// Number.prototype.minus = function minus(num) {
// return this - checkNum(num);
// }
// 若像写法2中所写的,全局的方法checkNum,可能会和其他方法冲突,所以可以使用闭包~function(){}()包起来
// 方法3
~ function anonymous(proto) {
const checkNum = function checkNum(num) { //此时的这个方法是属于私有的,不会与全局的冲突
// 不管是否是数字,强制转换为数字类型
num = Number(num);
if (isNaN(num)) {
num = 0;
}
return num;
}
// Number.prototype.plus = function plus(num) {
// return this + checkNum(num);
// }
// Number.prototype.minus = function minus(num) {
// return this - checkNum(num);
// } //Number.prototype使用多次,可以作为参数传进方法中 写法如下=>
proto.plus = function plus(num) {
return this + checkNum(num);
}
proto.minus = function minus(num) {
return this - checkNum(num);
}
}(Number.prototype)
let n = 10;
// n.plus(10); //为打印this而存在
let m = n.plus(10).minus(5);
console.log(m); //15 (10+10)-5
// "+" 左右其中一方有引号是字符串,就会进行字符串拼接
// Number的实例对象中,会先找valueOf找初始值,如(new Number(10)).valueOf(),找不到会toString() ,再进行运算
/*
创建一个数据类型值:(不论哪一种方式,创建出来的结果都是所属类的实例)
1、字面量方式
2、构造函数方式
*/
// 基本数据类型两种创建方式是不一样的,字面量创建的是基本类型值, 构造函数创建的是引用类型值。
// let a = 10;
// let b = new Number(10);
// console.log(b.valueOf() === a) //对象结果的原始值是基本类型数字10
</script>
</body>
</html>
2、写出下列代码运行结果,分析运行思路
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(); //'0'
my_fun.c(); //'30'
运行思路如下代码中注释所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>面试题2-重置类的原型指向</title>
</head>
<body>
<script>
// 2、画图计算下面的结果
/*
运行以下代码会创建一个堆,就是fun函数(堆) 里面的内容:
函数:代码字符串:“this.a = 0;this.b = function () {alert(this.a);}”
对象:键值对: name : fun,
prototype:fun.prototype(对象,也是个堆)
__pproto__:每个对象都有这个属性,是所属类的原型,是个函数,所以是Function.prototype
fun.prototype:是个对象,可称为堆AAAFFF111,内容为:
constructor:fun
__pproto__:因为是对象,没有new,不是哪个的实例,所以指向Object.prototype
*/
function fun() {
this.a = 0;
this.b = function () {
alert(this.a);
}
}
/*
fun.prototype = {...}这是自定义的对象,先执行"="右边的,因为等号优先级是右边优先,这个自定义对象的堆内容为:
b:function(){}..,
c:function(){}...
__proto__:不是某一个的实例,所以指向Object.prototype
在此时,赋值的时候,是在重置类的原型指向,fun.prototype不再指向上文中的AAAFFF111,而是此时的自定义对象,可称为AAAFFF222.
重置类的原型指向,带来的问题1:构造函数可能会丢失
为避免出现问题,在堆AAAfff222中需要手动设置构造函数,为constuctor:fun
*/
fun.prototype = {
// constuctor:fun, 构造函数可能会丢失,需要手动设置
b: function () {
this.a = 20;
alert(this.a);
},
c: function () {
this.a = 30;
alert(this.a);
}
}
/*
var my_fun = new fun();创建一个新的对象,堆可以称为AAAFFF333,是fun的实例,this指向my_fun内容为:
this.a = 0; => a : 0,
this.b = function(){alert(this.a)}
__proto__:指向所属类的原型,即fun.prototype,也就是AAAfff222
*/
var my_fun = new fun();
/*
my_fun.b(); => 私有函数b,
alert(this.a) => this指向my_fun, my_fun.a = 0;所以alert(),是字符串0=> '0'
*/
my_fun.b(); //'0'
/*
my_fun.c();=> my_fun中没有c方法,所以是向上级查找=> 共有函数c(){
this.a = 30;
alert(thisa)
} ,
=>this指向my_fun,所以,在此是修改堆AAAFFF333中a的值,
this.a = 30; => my_fun.a =30; =>a = 30;
alert(this.a); //'30'
*/
my_fun.c(); //'30'
</script>
</body>
</html>
3、写出下面代码执行输出的结果
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'
此题的思路在以下代码中:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>面试题3-写出下面代码执行输出的结果</title>
</head>
<body>
<script>
function C1(name) {
// =》name是undefined,没有给实例设置私有的属性name,会向__proto__原型上找,找到Tom
if (name) { //条件不成立
this.name = name;
}
}
function C2(name) {
this.name = name;
// 给实例设置私有属性name,不过值是undefined,=> this.name = undefined
}
function C3(name) {
this.name = name || 'join';
// 给实例设置私有属性name,不过值是undefined,=> this.name = undefined || 'join' ,左边为假,返回join
}
C1.prototype.name = 'Tom'; //所属类原型中公有属性name为Tom
C2.prototype.name = 'Tom';//所属类原型中公有属性name为Tom
C3.prototype.name = 'Tom';//所属类原型中公有属性name为Tom
alert((new C1().name) + (new C2().name) + (new C3().name));//'Tomundefinedjoin'
/*
(new C1().name) => 没有传参,name是undefined,里面的判断不成立,没有设置私有属性name => 会找原型上的'Tom';
(new C2().name) => 找私有属性name => undefined
(new C3().name) => 找私有属性name => 'join'
所以最后结果:
=> 'Tomundefinedjoin'
*/
</script>
</body>
</html>
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();
上述代码中,会涉及到运算符优先级,详见网址:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence;上述代码运行涉及思路:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>面试题4-运算符优先级</title>
</head>
<body>
<script>
/*
ECStack,执行环境栈:
EC(G):
VO(G):
变量提升:
function Foo(){...}... => Foo = 函数在变量提升时已经声明且赋值了,所以在此创建一个变量Foo,创建一个堆,称为AAAFFF000,并将这个堆赋值给Foo,AAAFFF000堆里面的内容为:
函数:代码字符串:
'getName=function(){console.log(1);};return this;'
对象:键值对:
name:Foo,
prototype:Foo.prototype,
__proto__:Function.prototype,
getName = function(){...} => 2
Foo的原型:Foo.prototype: 对象,可以称为堆BBBFFF000,内容为:
constructor:Foo,
__proto__: Object.prototype,
getName = function(){...} => 3;
var getName = function(){...} => 创建变量getName;在此时并未赋值;
function getName = function(){console.log(5)}; => 已经有定义过的getName,不需要重新定义,直接给全局的getName赋值,为一个函数,此时全局的getName的值为堆AAAFFF111,执行的话是输出5.
代码执行:
Foo.getName = function (){console.log(2)} => 把Foo作为一个对象,在堆AAAFFF000中添加一个方法,getName=function(){console.log(2)} => 输出2
Foo.prototype.getName = function(){console.log(3)} => 此代码是在Foo的原型中,添加一个方法,getName,即在堆BBBFFF000中添加一个方法getNAme=function(){console.log(3)};
var getName = function(){console.log(4)}=> var getName在变量提升阶段已经操作过一次,但是没有赋值,在此时要给全局的getName重新赋值,为堆AAAFFF222,输出的值为4,是输出4的函数。
Foo.getName(); => 把Foo当做普通对象来走,调用Foo中的getName方法,是私有的,直接输出2 ;
getName(); => 全局的getName,输出为4,也就是AAAFFF222的值;
Foo().getName(); => 即先执行Foo(),然后执行”结果.getName()“; Foo()中,没".",this是window,作为函数来说,Foo中,getName不是自己私有的,所以要向上级作用域找,是全局的,将全局的getName的值改为堆AAAFFF333,输出的值为1,是输出1的函数。然后return this,就是return window,所以window.getName()是找的全局的,所以输出结果为1
getName() => 全局的getName,所以值为1
new Foo.getName(); => 根据运算符优先级,new (无参数列表)的优先级是18,而Foo.getName,成员访问(带点的)的优先级为19,所以根据优先级的大小,应该先执行Foo.getName,然后执行new (Foo.getName)(), Foo.getName函数的值是2,new(Foo.getName)(),创建实例的时候会将这个函数执行,所以值为2
new Foo().getName(); => 根据运算符优先级,new (带参数列表)是19,成员访问也是19,所以是从左到右执行,所以会先new Foo(),new Foo()是创建Foo类的实例,然后看实例.getName(),先看自己里面有没有getName,没有的话通过原型链去上级查找,在Foo()中没有this.getName=...,所以实例中的Foo()是没有这个私有属性,因为实例中没有私有的getName属性,所以找原型上的方法,就是Foo.prototype.getName,输出为3。
new new Foo().getName(); => 与typeof typeof []一样,是先算tyoeof [],然后将这个结果再算 typeof 右边的结果,
所以这个是先new Foo(),因为带"()"了是有参数列表,是19,而".getName()"成员访问优先级也是19 ,所以从左向右执行。
=> new new Foo().getName(); 所以先执行new Foo(),创建一个Foo()的实例;
-> new 实例.getName(); 因为实例,后面是没有参数列表,运算值是18,没有后面的"."成员访问(优先级值为19)的优先级高,所以执行实例.getName,但是因为在实例中没有私有属性getName,所以通过原型链向上级查找,就是原型上的getName;
-> new 原型上的getName(); 就是new function () {console.log(3);} ,执行把它当做普通函数执行,并且返回这个函数的实例,无什么用,但是会执行此函数,所以输出为3.
*/
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();
// 运算符优先级:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
</script>
</body>
</html>
5、a等于什么值,会让下面条件成立
var a = ? ;
if (a == 1 && a == 2 && a == 3) {
console.log('OK');
}
此题的解法如下代码所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>面试题5-面向对象的新玩法</title>
</head>
<body>
<script>
/*
面试题5:a等于什么值,会让下面条件成立
var a = ?;
if (a == 1 && a == 2 && a == 3) {
console.log('OK');
}
*/
/*
== 相等 与 === 绝对相等 对比
1、左右数据值类型不同,三个等号不能转换数据类型,直接返回false,但是两个等于号会默认先转换为一致的数据类型在进行比较。
NaN==NaN:NaN和谁都不相等(包括自己)。
null==undefined:null和undefined两个等号比较是相等的(三个则不相等),但是他们和其他任何值都不相等。
对象==字符串:把对象转换为字符串(对象.toString())。
剩余的情况都是转换为数字(对象转换为数字:Number(对象.toString());字符串转数字:Number(字符串))。
*/
/*
方案一:利用比较的时候默认会转化为字符串的机制,通过重写toString()来完成需求
*/
/*var a = {
i: 0,
toString() {
return ++this.i;
}
};
// => a==1:a.toString()
if (a == 1 && a == 2 && a == 3) {
console.log('OK');
}
*/
// 方案一的第二种写法
// var a = [1, 2, 3];
// a.toString = a.shift;
// if (a == 1 && a == 2 && a == 3) {
// console.log('OK');
// }
/*
方案二:Object.defineProperty劫持对象中某个属性的操作
*/
// 全局变量也是给window设置一个全局属性
var i = 0;
Object.defineProperty(window, 'a', {
get() {
// => 获取window.a的时候触发
return ++i;
},
set() {
// => 给window.a设置属性值得时候触发
}
});
if (a == 1 && a == 2 && a == 3) {
console.log('OK');
}
</script>
</body>
</html>
6、下面代码输出结果是什么?为什么
let obj = {
2: 3,
3: 4,
length: 2,
push: Array.prototype.push
};
obj.push(1);
obj.push(2);
console.log(obj);
此题答案如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>面试题6-关于面向对象的问题</title>
</head>
<body>
<!-- 面试题6-下面代码输出结果是什么?为什么? -->
<script>
/*
模拟Array.prototype.push方法
Array.prototype.push = function push(){
// => this :arr
// this.length = this.length || 0;
// => 拿原有的length作为新增项的索引
this[this.length] = num;
// => length的长度是会自动跟着累加1
};
*/
// let arr = [10, 20];//=> {0:10,1:20,length:2}
// arr.push(30);
let obj = {
2: 3,
3: 4,
length: 2,
push: Array.prototype.push
};
obj.push(1); //=>obj[2]=1 obj.length=3
obj.push(2);//=>obj[3]=2 obj.length=4
console.log(obj); //{2: 1, 3: 2, length: 4, push: ƒ}
// 若对象中没有length,会自动转化为0
let obj2 = {
1: 10,
push: Array.prototype.push
};
obj2.push(20); //=> obj[obj.length]=20; 如果length为undefined,那么会自动转化为索引为0:obj[0]=20;
console.log(obj2);//0: 20, 1: 10, length: 1...
</script>
</body>
</html>