10.7.JS-练习题

207 阅读4分钟

1. 预编译问题:相同函数名的函数,后面的函数会覆盖前面的函数

var x = 1, y = z = 0;
function add(n) {
  return n = n + 1;
}
y = add(x);
function add(n) {
  return n = n + 3;
}
z = add(y);
// x: 1
// y: 4
// z: 7

2. 函数体直接调用报错问题解决

  • 将函数用括号包括起来,解析器会把函数解析为函数表达式,而函数表达式可以直接调用
  • 在函数体前添加 ! + - ~ 符号也可以运行函数
// 不会报错,因为后面括号会被认为运算符
function test(){

}(123)
// 会报错,函数体无法调用,只有表达式可以调用,使用括号包起来可以调用,在函数体前添加 `!` `+`  `-` `~` 符号
function test(){

}()

3. this指向

function foo(){
  bar.apply(null,arguments);
}
function bar(){
  console.log(arguments); // [1,2,3,4,5]
}
foo(1,2,3,4,5);

4. n进制转换10进制

parseInt(3, 8); // 8进制的3, 转换为10进制还是3
parseInt(3, 2); // 2进制的3, 3在2进制不存在, 转换为10进制为NaN
parseInt(3, 0); // 0进制的3, 3在0进制不存在, 转换为10进制为NaN或报错
// 结果:
// 3, NaN, NaN
  • 进制转换 n进制转换为10进制
/* 10进制
1 = 1
10 = 10 
100 = 10 ^ 2
1000 = 10 ^ 3
10000 = 10 ^ 4
2进制
1 = 1
10 = 2 
100 = 2 ^ 2
1000 = 2 ^ 3
10000 = 2 ^ 4
 */

5. typeof返回结果

"function" "object" "undefined" "string" "number" "boolean" 

6. 实参列表和形参映射关系

  • 实参列表值修改,相应的形参值也会修改
  • 形参值修改,实参列表相应的值也会修改
function b(x, y, a) {
  arguments[2] = 10;
  console.log(a); // 10
}
b(1, 2, 3);

function b(x, y, a) {
  a = 10;
  console.log(arguments[2]); // 10
}
b(1, 2, 3);

7. 对括号内逗号隔开的函数执行,会执行后面的的函数

var f = (
  function f() {
    return "1";
  },
  function g() {
    return 2;
  }
)();
console.log(typeof f); // number

8. 函数执行后销毁

// 使用if后括号添加函数体会执行, 然后销毁函数, 在if大括号内f已经销毁, 没有定义, 所以typeof f得到 "undefined", 拼接x得到 "1undefined"
var x = 1;
if(function f(){}){
  x += typeof f;
}
console.log(x); // "1undefined"

9. 手写isNaN

// {} == {} --> false 对象比较是地址,每个对象的引用地址是不同的
// 通过Number返回是否为NaN, NaN无法进行比较, 所以使用空字符串拼接进行比较判断返回truefalse
function isNaN(num){
  var ret =  Number(num);
  ret += "";
  if(ret == "NaN"){
    return true;
  }else{
    return false;
  }
}

10. new 改变 this指向

function test(c){
  // var this = Object.create(test.prototype); 如果对test 进行 new 操作后, this指向实例,  如果没有this指向window 
  // 未 new 构造函数时:
  // var this = {
  //   __proto__: Object.prototype
  // }
  var a = 123;
  function b(){

  }
  console.log(this);
  // return this; 隐式返回 this
}
// AO {
//   arguments: [1], // 实参列表
//   this: window, // 此时this指向window
//   c: 1, // 形参和定义变量的函数是一样的
//   a: undefined, // 函数执行前 对定义的变量提前并赋值undefined
//   b: function(){}, // 函数执行前,对函数声明提前
// }
test(1); // 打印 window {}
var t = new test(); // 打印 test {}

11. this指向, 作用域链

var foo= '123';
function print(){
  var foo = '456';
  this.foo = '789';
  console.log(foo);// '456'
}
print();
console.log(foo); // '789'
// this指向, 作用域链
// 函数执行时,未改变 this 指向时,默认 this 指向 window , 在函数内定义变量, 变量存在于函数的局部作用域 AO 上, 取值时, 先查看自己作用域中的变量, 没有在向上查找, 找不到返回 undefined , 在函数中 对 this 进行添加属性相当于在全局中定义变量并赋值, 如果之前全局作用域中有这个变量, 则会修改变量的值, 最后函数中的打印foo为'456', 函数执行完后, 在打印foo, foo重新赋值变成了'789'

12. 打印值为什么? 预解析

function print(){
  console.log(foo); // undefined 预解析, 定义变量在函数运行前已经设为了undefined
  var foo = 2;
  console.log(foo); // 2 对变量进行赋值 2
  console.log(hello); // 报错 直接使用未定义变量, 浏览器停止执行
}
print();

13. 写出打印值 预编译 作用域

function print() {
  var marty = {
    name: 'marty',
    printName: function () { console.log(this.name) }
  }
  var test1 = { name: 'test1' };
  var test2 = { name: 'test2' };
  var test3 = { name: 'test3' };
  test3.printName = marty.printName;
  var printName2 = marty.printName.bind({ name: 123 });
  marty.printName.call(test1); // test1
  marty.printName.apply(test2); // test2
  marty.printName(); // marty
  printName2(); // 123
  test3.printName(); // test3
}

print();
// 预编译+执行阶段 AO 变化
// AO {
//   marty : {...},
//   test1: { name: 'test1' },
//   test2: { name: 'test2' },
//   test3: { name: 'test3' ,printName: function () { console.log(this.name) }},
//   printName2: function () { console.log(this.name) }.bind({name:123})
// }

14. 写出打印值 (函数执行, 闭包, 对象原型链)

var bar = { a: '002' };
function print() {
  bar.a = 'a';
  Object.prototype.b = 'b';
  return function inner() {
    console.log(bar.a);
    console.log(bar.b);
  }
}
print()(); // a,b 
// 函数执行 左->右
// 闭包: 函数作用域链 
// 对象中寻找属性, 先找自身, 在通过原型链向上寻找, 没有返回undefined