<script>
/* // 练习题1
// var a
console.log(a); // undefined
var a = 12 // 存在变量提升定义
function fn() {
// var a
console.log(a);
var a = 13 // 注意;这里的a,只会提升到方法作用域的顶部定义
// 方法中的var定义的变量也会提升,
// 且与全局变量同名时,将会提升到局部(方法)作用域的顶端
}
fn() // undefined
console.log(a); // 12
// 所以此处的a是全局变量a : var a = 12
*/
// 只有var定义的变量存在提升
/*
// 练习题2
// var a
console.log(a); // undefined
var a = 12 // 存在变量提升
function fn() {
// var a = 12
console.log(a);
a = 13
}
fn() // 12
console.log(a); // 13
*/
// 注意:当前作用域里面没有a,就回到上一层作用域里面去寻找a
/*
// 练习题3
console.log(a); // 报错:Uncaught ReferenceError(未捕获引用错误):Cannot access 'a' before initialization
let a = 12
function fn() {
console.log(a);
let a = 13
}
fn() // 报错:Uncaught ReferenceError:Cannot access 'a' before initialization
console.log(a); // 12
*/
// let 定义的变量不存在提升
// 注意:只要是在当前作用域里面存在的变量,就不会去上层作用域寻找了。
/*
// 练习题4
console.log(a); // Uncaught ReferenceError: a is not defined
a = 12 // 没有用var定义的变量不存在提升
function fn() {
console.log(a); // 报错,因为当前作用域里面有a,就不会调用外层的a
let a = 13 // 注意:这里的a的作用域是方法,并且不存在提升
}
fn() // Uncaught ReferenceError: Cannot access 'a' before initialization
console.log(a); // 12
*/
// 注意:变量定义时不使用任何关键字,该变量也不存在提升
// 定义变量可以不使用任何关键字,但是在定义该变量之前,不能使用该变量,否则报错
// 这是一种不规范的写法,错误:未捕获引用错误(未定义就进行使用)
/*
// 练习题5
console.log(a); // Uncaught ReferenceError:Cannot access 'a' before initialization
let a = 12 // 该变量不会提升
function fn() {
console.log(a); // 报错,因为当前作用域里面有a,就不会调用外层的a了
let a = 13 // 注意:这里的a的作用域是方法
}
fn() // Uncaught ReferenceError: Cannot access 'a' before initialization
console.log(a); // 12
*/
// 注意:作用域是以方法为单位的
// 总结:
// 先看变量是否有关键字定义
// 无关键字定义则不会提升且无作用域,(对代码块无任何影响)
// 再看定义变量的关键字为何,
// 为var则为全局变量,存在提升,作用域是全局
// 全局变量同名时,将会分别提升到局部(方法)作用域的顶端
// 为let则为局部变量,不存在提升,作用域是方法内部
// 提升就是代码块的执行顺序,提升将会提升至全局顶端或是方法定义顶端
// 即将会先行执行,再执行其他代码
/*
// 练习题1
var foo = 1
function bar() {
if(!foo){
var foo = 10 // 注意:该变量会提升到方法作用域的顶部定义
}
console.log(foo);
}
bar() // 10
// 相当于
var foo = 1
function bar() {
var foo // 此时foo的值为undefined
if(!foo){ // 所以此处 !foo = true
foo = 10 // foo = 10
}
console.log(foo); // 输出了10
}
bar() // 10
*/
// 注意:只要在作用域使用var定义的变量,一定会提升到当前作用域顶端定义。
/* // 练习题2
var foo = 1
function bar() {
if(!foo){
foo = 10 // 注意:这里的foo是全局的foo
}
console.log(foo);
}
bar() // 1 */
/* // 练习题3
var n = 0 // 这里定义的是全局变量n
function a() {
var n = 10 // 这里是a方法中定义的n
function b() {
n++
console.log(n);
}
b()
return b
}
var c = a() // 11 // a()方法return了b函数,再将其赋值给全局变量c
// 但是调用a()方法时,将会执行a()方法,所以b()方法此时已经被执行了一次,打印输出了n (n++ => n = 11)
c() // 12 // 此处再次调用c(),即是b()方法再次被调用了,输出了n (n++ => n = 11 + 1 = 12)
console.log(n); // 0 // 这里打印的是全局变量n */
// 注意:使用var定义的变量可以同名,但是需要通过位置来判断是否是全局变量。
/* // 练习题4
var a=10
var b=11
var c=12
function test(a){
a = 1 // 这里的a是方法的形参
var b = 2 // 这里的b是test方法里面的b
c = 3 // 但是这里的c与全局变量同名,所以这里的c是全局变量c
}
test(10)
console.log(a) // 10
console.log(b) // 11
console.log(c) // 3 */
// 注意:作用域看方法
/* // 练习题5
// var a
// console.log('a' in window); // true // in关键字,用于检查是否在指定对象中存在,返回值Boolean型
if(!('a' in window)){
var a = 10 // 注意:这里的a会提升为全局变量,提升至全局顶端定义
}
console.log(a) // undefined */
/* // 练习题6
var a = 4
function b(x, y, a) {
console.log(a) // 打印的是形参a (a = 3), 输出3
arguments[2] = 10 // 1,2,3,... => 1,2,10,...
// console.log(arguments); // Arguments(3) [1, 2, 10, callee: ƒ, Symbol(Symbol.iterator): ƒ]
console.log(a) // 再次打印形参a (a = 10), 输出10
}
a = b(1,2,3) // 注意:b方法没有返回值,返回值就默认为undefined
// 3
// 10
console.log(a) // undefined */
/* // 练习题7
var a = 9
function fn() {
a = 0 // 1 2 1 2
return function(b){
return b + a++ // 5 + 0 5 + 1 5 + 0 5 + 1
}
}
var f=fn()
console.log(f(5)) // 5
console.log(f(5)) // 6
console.log(fn()(5)) // 5
console.log(f(5)) // 6
console.log(a) // 2 */
/* // 练习题8
var ary = [1,2,3,4]
function fn(ary){
ary[0] = 0 // 0,2,3,4
ary = [0] // 0
ary[0] = 100 // 100
return ary // 100
}
var res = fn(ary)
console.log(ary) // [0,2,3,4]
console.log(res) // [100]
// 整个运算(执行)过程如下:
// 先在栈中创建一个全局变量ary,其内部存放的堆内存地址指向堆中新创建的数组1[1,2,3,4]
// 然后将fn(ary)的值传给变量res,这时候致命的重点就悄然而至了:
// 传的实参是全局变量ary赋值给形参ary,所以会在栈中创建一个新变量ary(形参),其内存地址也指向堆中的数组1[1,2,3,4]
// 由于 ary[0] = 0 导致 堆中的数组1变为[0,2,3,4],同时全局变量ary也是指向数组1,所以全局变量ary = [0,1,2,3]
// ary = [0] 导致 形参ary中存放的堆内存地址指向了新数组2[0],所以此时形参ary与全局变量ary不再指向同一个堆内存地址
// ary[0] = 100 致使 新数组2变为[100],即形参ary指向数组2[100],
// return ary 致使 返回的形参ary 赋值给了变量res,(本质上:将结果存放的内存地址拷贝一份给了res)
// 所以,res 指向 数组2[100],输出res时将输出[100]
// 全局变量ary 指向 数组1[1,2,3,4],当输出全局变量ary时将输出[0,2,3,4]
*/
/* // 练习题9
function fn(i){ // i => 10 => 20 => 30
return function(n){ // n => 20 => 40 => 50
console.log(n + (i++)) // 30 60 80 41
// console.log(n + i) // 30 60 80 41
// i++ // 11 12
// 注意:n + (i++) 中的 i++,无论是否有括号,都是先返回++前面的值,再进行自加1的操作。
}
}
var f = fn(10) // 所以此处i的值仍为10
f(20) //i = 10 , n = 20 => 20 + 10 => 30 , i = 11 // 30
fn(20)(40) // 40 + 20 => 60 // 60
fn(30)(50) // 50 + 30 => 80 // 80
f(30) //i = 11 => 30 + 11 => 41 // 41 */
// 注意:f()与fn()的方法区内存空间是彼此独立的
/* // 练习题10
var num = 10 // window.num = 10 => 65
var obj = { num: 20 } // obj.num = 20
obj.fn = (function (num) { // num = 20
this.num = num * 3 // window.num = obj.num * 3 = 20 * 3 = 60
num++ // num = 20 + 1 = 21
return function (n) {
this.num += n
num++
console.log(num)
}
})(obj.num) // 传参obj.num = num = 20
var fn = obj.fn // window.fn = obj.fn
console.log(fn) // window.fn = function (n){this.num += n;...}
fn(5) // window.fn(20)(5) => window.num + 5 = 65 , num = 21+1 = 22 // 22
obj.fn(10) // obj.fn(20)(10) => obj.num + 10 => obj.num = 30, num = 22+1 = 23 // 23
console.log(num, obj.num) // 65 30 */
/* // 练习题11
var fullName = 'language' // window.fullName = 'language'
var obj = {
fullName: 'javascript', // obj.fullName = 'javascript'
prop: { // prop{getFullName()}
getFullName: function() {
return this.fullName
}
}
}
console.log(obj.prop.getFullName()) // return this.fullName => prop.fullName = undefined => undefined
var test = obj.prop.getFullName //test = getFullName(){return this.fullName}
console.log(test()) //window.test() => this.fullName => window.fullName = 'language' => language */
/* // 练习题11-2
var fullName = 'language' // window.fullName = 'language'
var obj = {
fullName: 'javascript', // obj.fullName = 'javascript'
prop: { // prop{getFullName()}
getFullName: () => {
return this.fullName
}
}
}
console.log(obj.prop.getFullName()) // return this.fullName => window.fullName = 'language' => language
var test = obj.prop.getFullName //test = getFullName
console.log(test()) //window.test() => this.fullName => window.fullName = 'language' => language */
// 注意:箭头函数里面的this指向它外层方法的this,如果外层函数没有this或者没有外层函数,将会指向window对象
/* // 练习题12
var name = 'window' // window.name = 'window'
var Tom = {
name: "tom", // Tom.name = "tom"
show: function() {
console.log(this.name)
},
wait: function() {
var fun = this.show // fun = tom.show
fun()
}
}
Tom.wait() // var fun = console.log(this.name) => console.log(window.name) => window */
</script>