<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>你不知道的javascript(上卷)</title>
</head>
<body>
<script type="text/javascript">
/*
//9、this 的全面解析
this的绑定和函数声明位置没有任何关系,之取决于函数的调用方式
在理解this的绑定过程之前,首先理解调用位置:调用位置就是在代码中被调用的位置(问不是声明位置,)。只有仔细分析调用位置才能回答这个问题:这个this到底引用的是什么
通常来说,寻找调用位置就是寻找“函数被调用的位置”
2.1、函数调用位置
function baz(){
console.log("baz");
bar(); //bar的调用位置
}
function bar(){
console.log("bar");
foo();//foo的调用位置
}
function foo(){
console.log("foo");
}
baz();//baz的调用位置
b、函数调用位置2
function foo(){
console.log(this.a);
}
var a = 2;//2
foo();
function foo(){
"use strict";
console.log(this.a); //报错,严格模式下,this不会默认绑定给window
}
var a = 2;
foo();
function foo(){
console.log(this.a);
}
var a = 2;
(function(){
"use strict";
foo(); //2
})();
//三种情况下,只要函数执行不是执行在严格模式下,默认绑定才会绑定到全局上
2.2、绑定规则
2.2.1默认绑定
function foo(){
console.log(this.a);
}
var a = 2;
foo();//2
//非严格模式下,this默认绑定在window下
function foo(){
"use strict";
console.log(this.a);
}
var a = 2;
foo();// this is not defined;
//严格模式下不能将全局对象默认绑定,因此this会绑定到undefined上
function foo(){
console.log(this.a);
}
var a = 2;
(function(){
"use strict";
foo();//2
})()
//严格模式下的调用不影响默认绑定
2.2.2隐式绑定
function foo(){
console.log(this.a);
}
var obj = {
a : 2,
foo : foo
}
obj.foo();
//无论是直接在obj中定义还是先定义在添加为引用属性,这个函数严格来说不属于obj对象,
//调用位置会使得obj上下文来引用函数,因此你可以说函数被调用时obj对象“拥有”或者“包含”函数
function foo(){
console.log(this.a);
}
var obj2 = {
a : 42,
foo : foo
}
var obj1 = {
a : 2,
obj2 : obj2
}
obj1.obj2.foo();//42
//对象属性引用链中只有上一层或者说最后一层在调用位置中起作用。
//隐式丢失
function foo(){
console.log(this.a);
}
var obj = {
a : 2,
foo : foo
}
var bar = obj.foo;//函数别名
var a = "oops,global";
bar();//"oops,global";
2.2.3显示绑定
function foo(){
console.log(this.a);
}
var obj = {
a : 2,
}
foo.call(obj);//2
//通过foo.call(..),我们可以调用foo时强制把他的this绑定到obj上
//硬绑定
function foo(){
console.log(this.a);
}
var obj = {
a : 2,
}
var bar = function(){
foo.call(obj);
}
bar();//2
setTimeout(bar,1000);//2
bar.call(window);//2
//硬绑定的bar不可能在修改他的this
//创建函数bar(),并且在内部手动调用foo.call(obj),
//因此强制把foo的this绑定到obj,无论后面我们如何调用到bar(),
//他总会手动在obj上调用foo,这种绑定是一种显性绑定,因此我们称之为硬绑定
//API调用的‘上下文’
function foo(el){
console.log(el,this.id); //1 awesome 2 awesome 3 awesome
}
var obj = {
id : "awesome",
}
//调用foo()时把this绑定到obj
var arr = [1,2,3].forEach(foo,obj);
//console.log(arr);
new 绑定
使用new调用函数,会执行以下操作
1、创建(或者说构造)一个全新的对象
2、这个新对象会被执行[[Protoptye]]连接
3、这个新的对象会绑定到函数调用的this
4、如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新的对象
function foo(a){
this.a = a;
}
var bar = new foo(2);
console.log(bar.a);
2.3优先级
默认绑定 < 隐式绑定 < 显式绑定 < new
.
.
.
.
2.4被忽略的this
function foo(){
console.log(this.a);
}
var a = 2;
foo.call(null);//2
//call 参数为null或者undefined的时候会默认是全局this
柯里化。。。。
*/
/*
8、this词法
var foo = a =>{
console.log(a);
}
foo(2); //2
//箭头函数,是根据为层(函数或者全局)作用域来决定this
var foo1 = function(a){
console.log(a);
}
foo1(2);
// 两种声明是等效的
var obj = {
id : "awesome",
cool:function coolFn(){
console.log(this.id)
}
};
var id = "not awesome";
obj.cool(); //awesome
setTimeout(obj.cool,100); // not awesome
// setTimeout 导致 cool()函数丢失了同this之间的绑定
//解决方案
var obj = {
count : 0,
cool: function coolFn(){
var self = this;
if(self.count<1){
setTimeout(function timer(){
self.count++;
console.log("awesome");
},100);
}
}
}
obj.cool(); // awesome
*/
/*
//7、动态作用域
function foo(){
console.log(a);
}
function bar(){
var a = 3;
foo();
}
var a = 2;
bar(); //2 输出结果是2,词法作用域让foo()中的a通过RHS引用到了全局
//作用域中的a,因此会输出2
function foo(){
console.log(a); //a is not defined
}
function bar(){
var a = 3;
foo();
}
bar();
function foo(){
var a = 3;
console.log(a); //引用的是局部变量a的值
}
var a = 1;
foo();// 3
*/
/*
//5、闭包作用域
function foo(){
var a = 2;
function bar(){
console.log(a);
}
return bar;
}
var baz = foo();
baz(); //2 foo() 的返回值是bar()函数,所以通过baz()可以执行这个函数
function foo(){
var a = 2;
function baz(){
console.log(a);
}
bar(baz);
}
function bar(fn){
fn();
}
foo();//2 foo
for(var i = 0;i<=5;i++){
(function(){
var j = i;
setTimeout(function time(){
console.log(j); // 每100毫秒输出一个数,分别输出0,1,2,3,4
},j*100);
})()
}
for(var i = 0;i<=5;i++){
(function(j){
setTimeout(function time(){
console.log(j); // 每100毫秒输出一个数,分别输出0,1,2,3,4
},j*100);
})(i)
}
for(var i = 1;i<=5;i++){
(function(){
setTimeout(function time(){
console.log(i); // 每100毫秒输出一个数,分别输出6,6,6,6,6
},i*100);
})()
}
*/
/*
//4、提升 --先编译在执行
a = 2;
var a;
console.log(a);//a 因为var声明的变量存在提升
console.log(b); //undefined var声明存在提升,赋值不存在提升,所以b存在但是没赋值
var b = 2;
foo(); //函数竟然可以执行,说明函数声明也提升了
function foo(){
console.log(a); //undefined , 声明提升了,不存在赋值提升
var a = 2;
}
foo(); //1
var foo;
function foo(){
console.log(1);
}
foo = function(){
console.log(2)
}
//引擎编译如下
function foo(){
console.log(1);
}
foo(); //1
var foo = function(){
console.log(2)
}
foo(); //3
//var foo 尽管出现在在function之前,但是由于是重复声明,所以被忽略掉
//函数声明会被提升到普通变量之前
function foo(){
console.log(1);
}
var foo = function(){
console.log(2);
}
function foo(){
console.log(3);
}
foo(); // foo is not a function
var a = true;
if(a){
function foo(){
console.log("A");
}
}
else{
function foo(){
console.log("B");
}
}
*/
/*
//3.4.3、let和var的区别
for(var i = 0;i<10;i++){
}
console.log(i);// 10
for(let j=0;j<10;j++){
}
console.log(j);// j is not defined
//for循环结束后i并没有被销毁,导致全局污染
*/
/*
//3.3.2、立即执行函数:抱在一对括号内
var a = 2;
(function foo(){
var a = 3;
console.log(a); //3
})();
console.log(a);// 2
*/
/*
//块级作用域,函数作用域
//3.2、规避冲突
*/
/*
function foo(){
function bar(a){
i = 3;
console.log(a+i);
}
for(var i = 0;i<10;i++){
bar(i*2);
}
}
foo();//死循环,bar中的i覆盖了for循环中的i导致死循环
*/
/*
//2.2.1、eval
function foo(str,a){
eval(str);
console.log(a,b); //4
}
var b = 2;
var result = foo("var b = 3;",1);
console.log("result:",result); //undefined???默认严格模式?
//书上的结果是1,3因为eval(str)== var b = 3; 读取的是局部变量3
function foo(str){
"use strict";
eval(str);
console.log(a);
}
foo("var a = 2");//a si not defined
*/
</script>
</body>
</html>