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
/* 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无法进行比较, 所以使用空字符串拼接进行比较判断返回true或false
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