看js代码说结果并分析
11. 原型与原型链
function fun() {};
fun.prototype.a = 1;
let f1 = new fun();
fun.prototype = {b: 2, c: 3};
let f2 = new fun();
fun.prototype.d = 4;
console.log(f1.a);
console.log(f1.b);
console.log(f2.c);
console.log(f2.d);
答案:
1
undefined
3
4
解析:
- 考察原型链知识点;
- 下面进行代码运行过程分析:
function fun() {};
fun.prototype.a = 1; // 在原型对象上添加属性a,并赋值为:1
let f1 = new fun(); // 实例化对象f1,此时`f1.__proto__`指向`fun.prototype`
fun.prototype = {b: 2, c: 3}; // fun的属性指向新的对象,但不影响f1实例对象,f1.__proto__还是指向原来哦的原型对象
let f2 = new fun(); // 实例化对象f2,此时`f2.__proto__`指向`fun.prototype`是个新对象,即:{b: 2, c: 3}
fun.prototype.d = 4; // 在fun原型对象设置属性b并赋值为:4
// 此时fun.prototype指向的对象为:{b: 2, c: 3, d: 4}
// 同时f2.__proto__指向fun.prototype,也指向{b: 2, c: 3, d: 4}
console.log(f1.a); // 1
console.log(f1.b); // undefined,f1自身和原型上都没有属性b
console.log(f2.c); // 3
console.log(f2.d); // 4
12. this指向的问题,箭头函数和普通函数的区别
var num = 1;
let obj = {
num: 2,
foo: function () {
console.log(this.num);
},
foo2: () => {
console.log(this.num);
}
};
let f1 = obj.foo;
let f2 = obj.foo2;
obj.foo();
obj.foo2();
f1();
f2();
答案:
2
1
1
1
解析:
// 考察`this`指向、箭头函数和普通函数的区别
var num = 1; // 声明全局变量num赋值1,会挂载到window对象上
let obj = {
num: 2,
foo: function () { //这是一个普通函数,this指向在其执行时才被确定下来
console.log(this.num);
},
foo2: () => { // 这是一个箭头函数,this指向在定义时就已确定;
console.log(this.num); // 内部的this指向定义时的this,外部this指向全局作用域this即window
}
};
let f1 = obj.foo;
let f2 = obj.foo2;
obj.foo(); // 执行obj内部的普通foo()函数,故输出:2
obj.foo2(); // 执行obj内部的箭头foo2()函数,当前运行在全局作用域上,故输出:1
f1(); // 在全局对象上调用,故输出:1
f2(); // 箭头函数,故输出:1
13. 原型链、Function、Object的关系
Object.prototype.a = 1;
Function.prototype.b = 2;
function fun() {};
var f = new fun();
console.log(fun.a);
console.log(fun.b);
console.log(f.a);
console.log(f.b);
答案:
1
2
1
undefined
解析:
概念理解:
- typeof Function === 'function';
- Function.proto === Function.prototype;
- Function.prototype.proto === Object.prototype; // 函数本身是一个对象,即Function拥有Object对象上的属性
Object.prototype.a = 1; // 在原型对象上添加属性并赋值
Function.prototype.b = 2; // 同上
function fun() {};
var f = new fun();
/** 原型链指向
* fun.__proto__ => fun.prototype
* Function.prototype.__proto__ => Object.prototype
* */
console.log(fun.a); // 由此顺着原型链找到了Object.prototype.a为:1
console.log(fun.b); // 同上,找到Function.prototype.b为:2
/** 原型链指向
* f.__proto__ => fun.prototype
* fun.prototype.__proto__ => Object.prototype (fun.prototype 只是一个普通的对象)
* */
console.log(f.a); // 由此顺着原型链找到了Object.prototype.a为:1
console.log(f.b); // 原型链同上,但找不到属性b,所以结果为:undefined
14. &&和||运算符的理解
var a = 2 >= 3 || true && 1 || false;
console.log(a);
答案:
1
解析:
计算步骤如下:
var a = 2 >= 3 || true && 1 || false;
// a = 2 >= 3 || 1 || false // &&优先级比false高,&&左右都成立输出右边合集
// a = 1 || false
// a = 1
15. switch和自增运算符
var x = 0;
switch (++x) {
case 0: ++x;
case 1: ++x;
case 6: ++x;
}
console.log(x);
答案:
3
解析:
var x = 0;
switch (++x) { // 入参前x=0,并自增到:1
case 0: ++x; // 不匹配
case 1: ++x; // 匹配,x自增到:2,并且没有brek,向下执行
case 6: ++x; // 由于没有brek,x继续自增到:3
}
console.log(x); // 输出:3
16. 链式赋值运算
let a = {n: 1};
let b = a;
a.x = a = {n: 2};
console.log(a.x);
console.log(b.x);
答案:
undefined
{n: 2}
解析:
let a = {n: 1};
let b = a; // b 指向 {n: 1}
/**
* 成员访问(.运算符)优先级高于赋值运算符
* 首先找到a.x等待赋值,即对象{n: 1}的属性 x
* 然后运行表达式进行赋值,但不会影响b
* 赋值表达式本身的值要赋值的值,所以a = {n: 2}得到 {n: 2}
* 所以{a: 1}的属性x被赋值为{n: 2}
* **/
a.x = a = {n: 2};
console.log(a.x); // a被赋值为{n: 2},该对象没有属性x,输出:undefined
console.log(b.x); // b.x 即{n: 1}的属性x,故输出{n: 2}
17. 对象的属性名是什么类型?
let a = {};
let b = '123';
let c = 123;
a[b] = 'b'; // a => {'123': 'b'}
a[c] = 'c'; // a => {123: 'c'}
console.log(a[b]); // {'123': 'b', 123: 'c'}[123] 输出:'c'
答案:
c
解析:
考察对象的属性名、键访问 例子:
let obj = {a: 1}
obj.a // 属性访问
obj['a'] // 键访问
在对象中,属性名永远都是字符串类型,如果键名是非字符串也会默认转为字符串
let a = {};
let b = '123';
let c = 123;
a[b] = 'b'; // a => {'123': 'b'}
a[c] = 'c'; // a => {123: 'c'}
console.log(a[b]); // {123: 'c'}[123] 输出:'c'
18. 如果传递给函数的参数是引用类型,有什么特征
function user(obj) {
obj.name = 'a';
obj = new Object();
obj.name = 'b';
}
let pereson = new Object();
user(pereson);
console.log(pereson.name);
答案:
a
解析:
function user(obj) {
// obj 指向person引用的对象{}
obj.name = 'a'; // obj parson 引用的对象变为{name: 'a'}
obj = new Object(); // obj 引用新对象,对person无影响
obj.name = 'b'; // obj => {name: 'b'},对person无影响
}
let pereson = new Object(); // pereson => {}
user(pereson);
console.log(pereson.name); // pereson => {name: a}
19. es6新类型Symbol,作为对象属性的标识符
var a = {};
var b = Symbol('123');
var c = Symbol('123');
a[b] = 'b';
a[c] = 'c';
console.log(a[b]);
答案:
b
解析:
考察Symbol类型,一个symbol值能作为对象属性的标识符, 且这个标识符是唯一的,这是es6新增symbol的目的
var a = {};
var b = Symbol('123');
var c = Symbol('123');
a[b] = 'b'; // a => {Symbol(123): 'b'}
a[c] = 'c'; // a => {Symbol(123): 'b', Symbol(123): 'c'}
console.log(a[b]); // 故输出:b
20. 对象可以作为另外一个对象的属性名吗?
var a = {}, b = {k: 1}, c = {k: 2};
a[b] = 'b';
a[c] = 'c';
console.log(a[b]);
答案: c
解析:
/**
* 考察对象的属性、隐式强制类型转换
* 1. 对象的属性名只能是 string 或 symbol 类型
* 2. 如果对象的属性名不是这两者,则会将其隐性式转化为 string
* 对于非字符串类型转化为字符串类型
* 基本数据类型的转换:通常直接加引号(如:ture => 'true'),极小或极大值采用指数形式夹引号
* 对象类型的转换:如果对象的valueOf方法返回的是基本类型,就按valueOf返回值转换;否则就按对象的toSting方法返回值进行转换
* 否则就按照对象的突视性方法返回直来转换
* */
var a = {}, b = {k: 1}, c = {k: 2};
a[b] = 'b'; // a => {[Object Object]: b}
a[c] = 'c'; // a => {[Object Object]: c}
console.log(a[b]); // 输出:c
console.log(a[{}]); //等价上面,也是输出:c