看js代码说结果并分析(11-20)

104 阅读5分钟

看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

解析:

  1. 考察原型链知识点;
  2. 下面进行代码运行过程分析:
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

解析:

概念理解:

  1. typeof Function === 'function';
  2. Function.proto === Function.prototype;
  3. 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