js变量提升基础
知识点1:
if (false) {
var a = 1;
}
console.log(a); // undefiend
// 其实 var a 是执行了的, 只是if (false) a = 1; 这个赋值操作没执行。
知识点2:
function test() {
console.log(1);
}
var test;
console.log(test); // function test() {...}
// 是不是惊呆啦! 为什么 var test; 却没有覆盖掉函数,
// 只var不赋值不是为 undefined么!
// 其实直接 var test 和 var undefined不太一样
// 前者为无意义的声明,如果该变量有值, js 是不会处理这样的声明的。
// 举个例子
console.log(test); // 函数体
var test = 1;
function test() {
console.log(1);
}
// 因为函数提升优先级最高, 所以提升后的代码为
function test() {
console.log(1);
}
var test;
console.log(test);
test = 1; // 很显然答案是函数体啦
变量提升以及函数声明提升的优先级
alert(a);
a();
var a = 3;
function a() {
alert(10);
}
alert(a);
a = 6;
a();
答案
- 弹出函数体 function a() { alert(10); }
- 10
- 3
- a is not a function
注解: ==函数声明提升的时候连着函数体一起提升, 且优先级大于变量声明,也就是a函数声明会提到最前面, 具体的指向步骤是这样的
function a() { alert(10); }
var a;
a = 3;
alert(a);==
函数声明的作用域问题
(function test () {
console.log('i am test');
})();
console.log(test);
答案
test is no defiend
注解: 函数声明只能在当前作用域生效。特别的一点是es6花括号作用域, 只能使let, const失效。比如:
{
let a = 1
function A () {
console.log('AAA');
}
}
console.log(a); // undefined
A(); // AAA
函数名与变量引用的冲突
const fn = function test () {
console.log('i am test');
}
console.log(test);
答案
test is no defiend
注解: 把函数声明到一个变量上时, 对函数命名是没有作用的;
变量表达式声明的函数的函数名在函数内部不能被重写
(function test2 () {
test2 = 1;
console.log(test2)
})();
test2();
const fn = function test () {
test = 1;
console.log(test);
}
fn();
答案
- function test () { test = 1; console.log(test); }
- test2 is not defined
- function test () { test = 1; console.log(test); }
注解: ==函数表达式定义的时候==, ==函数的名字==外部不能访问,内部也不能修改。 什么是函数表达式, 以上两种都是, 和函数声明的区别就是函数表达式不以function开头定义的。 对比下面的写法:
// 函数声明
function zhijia() {
zhijia = "老师";
console.log(zhijia)
}
zhijia(); // 老师
来点花样
(function test (w) {
})(window);
console.log(test);
(function test2 (w) {
this.a = 2;
w.b = 1;
})(window);
console.log(a);
console.log(b);
答案
- test2 is not defined
- 2
- 1
注解: window传进自执行函数 但是test2这个函数并没有绑定在window上。自执行函数的this本身就指向window。
自执行函数的this指向
obj = {
fn: (function() {
console.log(this)
})()
}
console.log(obj.fn)
答案
- windows对象
- undefined
注解: 非严格模式下, 自执行函数的this永远指向window
自执行函数的this指向
obj = {
fn: (function() {
// "use strict";
console.log(this);
})()
}
console.log(obj.fn)
答案
- windows对象
- undefined
注解: 非严格模式下, 自执行函数的this永远指向window。打开注释试一下~
[你不知道的js] 书中一处错误记录
function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log(this.a);
}
foo();
答案
- undefined
注解: 是不是很诧异为什么输出的是undefinede而不是a is not defined; 因为此时输出的是this.a, this -> window, 也就是访问的window.a。对象下面的属性读不到会输出undefined, 尝试下改成console.log(a)~
this特点 谁调用指向谁 没人调用指向window
this.a = 20;
var test = {
a: 40,
init: () => {
console.log(this.a);
function go() {
// this.a = 60;
console.log(this.a);
}
go.prototype.a = 50;
return go;
}
}
// var p = test.init();
// p();
new (test.init())();
// 请写出答案 放出注释后 再写出下一版答案
答案
- 20
- 50
放开注释后 答案为
- 20
- 60
注解: 非严格模式下,箭头函数的this永远指向他调用地方的上下文this, (es6对象方法声明函数和箭头函数不能被new调用), test.init中的go方法, 因为没有调用者, this指向window。
补充-> 箭头函数this指向
const obj = {
a: 2
}
const fn = function () {
const k = () => {
console.log(this);
}
k();
setTimeout(() => {
console.log(this);
}, 1000)
}
fn.bind(obj)();
// fn.apply(obj);
// fn.call(obj);
答案
- {a: 2}
- {a: 2}
注解: 非严格模式下,箭头函数的this永远指向他调用地方的上下文this
new遇到es6函数!!!
var o = {
foo: function () {
console.log('jc');
},
bar() {
console.log('yd');
}
}
var f = o.foo.bind({});
new f();
var b = o.bar.bind({});
new b();
答案
- jc
- b is not a constructor
注解: es6对象方法声明方法和箭头函数不能被new调用
bind 能改变箭头函数的this指向么
var obj1 = {
a: 2
}
var fn1 = function () {
var k = () => {
console.log(this);
}
k.bind({ b: 1 })();
// k.bind(null)();
}
fn1.bind(obj)();
答案
- { a: 2 }
注解: 箭头函数没有this。
bind遇到new
this.a = 20;
function test() {
console.log(this.a);
}
var obj = {
a: 30
}
var result = test.bind(obj);
new result();
答案
- undefined
注解: new使bind失效。 new 绑定 > 显式绑定(bind, apply, call) > 隐式绑定(函数更改this指向) > 默认绑定
开胃菜
var num = 1;
function fn1() {
"use strict";
console.log(this.num++);
}
function fn2() {
console.log(++this.num);
}
(function() {
"use strict";
console.log(this);
fn2();
})();
fn();
答案
- undefined
- 2
- undefined
注解: 自执行函数的this非严格模式下永远指向window, 严格模式下为undefined, 而没有东西的调用fn2, 因为它没有调用者, fn2内部this指向window, 所以第一题是2, 第二题严格模式下没有window, 所以为undefined; 注意严格模式的作用范围, 只是对当前作用域有效。自执行函数的 "use strict"并不会作用到fn2函数内部。
function fn() {
console.log(this);
(function() {
console.log(this);
})();
}
fn.bind({ a: 3 });
// { a: 3 }
// window
原型链权重问题
function c1 (name) {
if (name) this.name = name;
}
function c2 (name) {
this.name = name;
}
function c3 (name) {
this.name = name || 'fe';
}
c1.prototype.name = 'shunshun';
c2.prototype.name = 'ys';
c3.prototype.name = 'nice';
console.log(new c1().name);
console.log(new c2().name);
console.log(new c3().name);
答案
- 'shunshun'
- undefined
- 'fe'
注解: 最有意思的是第二个打印的结果, 注意传入的是undefined。
杂耍
1. {} + [];
2. [] + {};
3. 1 + [];
4. 1 + {};
5. {} + [1, 2, 3];
6. [1, 2, 3] + {};
7. [] == 0;
8. [] === 0;
9. [[['a']]] == 'a';
10. [[['a']]] === 'a';
11. [[['a']]] === "a";
答案
- 0
- "[object Object]"
- "1"
- "1[object Object]"
- "NAN"
- "1,2,3[object Object]"
- true
- false
- true
- false
- true
注解:
- { } 是空代码块, 什么都没干, 数组隐式调用自身的toString方法, 所以打印1相当于 + ( [].toString() ), 相当于+ "", + 使"" 转化成整数0, 故为0;
- 如上, 这句相当于一个[].toString() + {}.toString(); 相当于"" + "[object Object]"; 故答案为 [object Object]; 这里需要注意的是中括号里的第一个object没有任何意义。仅仅是js为了区分其他类型给的一个标识(typeof显示的)。
- 略
- 需要注意的是, +"1" = 1; +"" =0; 但是+"1,2" = NAN;
- 略
- 略
...
杂耍
function test(m) {
m = { v: 5 }
}
var m = { k: 30 };
test(m);
alert(m.v);
答案
- undefined
注解: 把test形参m改成n就明白了; 必须滴~ 不解释。
函数声明提升的范围
function test() {
console.log(1);
}
(function() {
if (false) {
function test() {
console.log(2);
}
}
console.log(test); // 提升了 值为undefined
test();
})();
答案
- test is not a function
注解: 自执行函数在自己内部相当于声明了test, 但是因为逻辑(if (false)), 导致函数体提升不出来。而在自执行函数里面访问test, 因为找到一个没有赋值的声明, 所以不往上去查找window里的test, 如果改成如下就可以了。
function test() {
console.log(1);
}
(function() {
if (false) {
function test() {
console.log(2);
}
}
window.test(); // 1
})();
// 或者
function test() {
console.log(1);
}
(function() {
// if (false) {
// function test() {
// console.log(2);
// }
// }
console.log(test); // 内部找不到 往父层找 func test
test(); // 1
})();