作用域 的问题竟然只有你一个

2020.09.20
评论

作用域篇

作用域,啊?作用域,啊!那没事了

1)

var a = 2
function P() {
	this.a = 5
	this.fn = function() {
		console.log(this.a)
	}
}
var obj1 = new P()
obj1.fn() //5 对象上执行,this指向对象obj
var obj2 = new P().fn
obj2()  //2 全局中执行,this指向全局window

js函数this始终指向执行它的地方

class Animal {
	constructor(name) {
		this.props = {
			name
		}
	}
	fly() {
		function canFly() { // 单独的函数作用域
            // 此处的this为undefined
			return this.props.name === "birds"; 
		}
		if (canFly()) {
			console.log("i can")
		} else {
			throw new error("no")
		}
	}
}
const animal = new Animal('birds')
try {
	animal.fly() // 执行时遇见报错,自断跳到catch
} catch (e) {
	console.log(e) // 执行,结果为TypeError: Cannot read property 'props' of undefined
}

区分指向和作用域

3)

var i = 5;
function fn(i) {
	return function(n) {
		console.log(n + (++i))
	}
}
var f = fn(1) // i=1
f(2) // 闭包常驻,i为1,则输出4,f中i变成2
fn(3)(4) // 新闭包空间,输出8
fn(5)(6) // 同上,输出12
f(7) // 使用f的闭包空间,输出10,f中i变成3
console.log(i) // 5,没有操作过外部i

++i先加后赋值;闭包值常驻

4)

var x = 10;
function fn() {
    console.log(x); // 函数内使用非定义在函数内的变量时,会在定义处往上层找,直到顶层停止
}
function show(f) {
    var x = 20;
    f();
}
show(fn); // 输出10
let a = 0,
	b = 0;
function A(a) {
	A = function(b) {
		console.log(a + b++)
	}
	console.log(a++)
}
A(1) // 这一步将外部A方法覆盖为内部A方法,输出1,a变成2
A(2) // 这一步实则使用内部A方法,a为内部A方法定义地方往上找,找到a=2;输出 4

i++选赋值后加;函数内使用非定义在函数内的变量时,会在定义处往上层找,直到顶层停止

5)

function Person() {
    //0
    getAge = function () {
        console.log(10)
    }
    return this;
}
 
//1
Person.getAge = function () {
    console.log(20)
}
 
//2
Person.prototype.getAge = function () { 
    console.log(30)
}
 
//3
var getAge = function () { // 变量提升
    console.log(40)
}
 
//4
function getAge() { // 变量提升,优先于方法3 
    console.log(50)
}
 
Person.getAge(); // 执行方法1,输出20
getAge(); // 执行方法3,输出40
Person().getAge(); // 执行方法0,且将方法3覆盖为方法0,输出10
new Person.getAge(); // 实例化方法1,输出20
getAge(); // 执行已经变成方法0的方法3,输出10
new Person().getAge(); // 实例化Person,调用其原型上的方法2,输出30

new xx.xxx()和new xx().xxx()是两个东西,前者实例化xx上的xxx,后者执行实例化xx后原型上的xxx方法;仔细看

6)

function sidEffecting(ary) {
	ary[0] = ary[2];
}
function bar(a, b, c) { //操作一:改成 (a,b,c=4) 会怎样
	c = 10 // 将arguments[2]值赋为10,[2,2,10]
	sidEffecting(arguments); // 将arguments[2]值赋值给arguments[0],[10,2,10]
	return a + b + c;
}
console.log(bar(2, 2, 2)) // 22
 
// 如果执行操作一
// 将c定义在函数参数的作用域中,外部无法改变,c覆盖为与arguments无关的内容,输出14
var x = 1;
function foo(x, y = function() { x = 2; }) { //传参内单独一个作用域空间
  x = 3; // 这里的x覆盖内部x
  //var x = 3
  y(); // 由于内部x与y方法定义在传参的作用域空间,所以覆盖内部x
  console.log(x); // 输出2
}
foo() 
console.log(x); // 输出1
 
// 如果将函数内部的x=3改为var x=3;
// 内部重新声明了一个x,导致y方法无法修改新x的值,使用内部输出3,外部仍然输出1

对于有默认值的参数,对应的arguments属性则无法被赋值;函数的传参位置会单独开辟一个作用域空间

7)

function fun(n,o) {
  console.log(o)
  return {
    fun:function(m){
      return fun(m,n);
    }
  };
}
var a = fun(0); // 创建一个数据空间a,输出undefined,n=0
a.fun(1); // 使用空间a,输出0
a.fun(2); // 使用空间a,输出0
a.fun(3); // 使用空间a,输出0
var b = fun(0).fun(1).fun(2).fun(3); // 链式后方法使用前一个方法创建的空间,最终创建空间b,依次输出 undefined 0 1 2 
var c = fun(0).fun(1); // 同上,最终创建空间c,输出 undefined 0,n=1
c.fun(2); // 使用空间c,输出1
c.fun(3); // 使用空间c,输出1

链式调用会创建一个共享的空间,类似于闭包

8)

var num = 1;
var myObject = {
    num: 2,
    add: function() {
        this.num = 3;
        (function() {
            // this为外层函数执行地方的外层
            console.log(this.num); 
            this.num = 4;
        })();
        console.log(this.num); 
    },
    sub: function() {
        console.log(this.num)
    }
}
myObject.add();  // add的this指向myObject,add内自执行函数this指向定义位置执行地方的外层,此处为window,输出 1 3,myObject.num=3 全局num=4
console.log(myObject.num);  // 3
console.log(num); // 4
var sub = myObject.sub;  
sub(); // 全局执行,this指向window,输出4

在函数内定义且在同一地方执行的内层函数(包括自执行函数),在调用时会默认执行在外层函数执行区域