Open Twlig opened 2 years ago
任何定义在函数或块中的变量,都可以认为是私有的,因为在这个函数或块的外部无法访问其中的变量。私有变量包括函数参数、局部变量,以及函数内部定义的其他函数。
function add(num1, num2) {
let sum = num1 + num2;
function addSub() {
console.log(sum);
}
return sum;
}
在这个函数中,函数 add()有 3 个私有变量:num1、num2 、 sum和内部函数addSub。这几个变量只能在函数内部使用,不能在函数外部访问。
⚠️:采用闭包的特性可以很好的解决外部想要访问函数私有变量的问题。
特权方法(privileged method)是能够访问函数私有变量(及私有函数)的公有方法。
下面代码中的getName和setName就是特权方法,也是实例方法。getName和setName是在构造函数中实现的,因此需要new一个实例才能访问。
function Person(name) {
this.getName = function() {
return name;
}
this.setName = function(value) {
name = value;
}
}
let p1 = new Person("zzy");
p1.getName(); //zzy
let p2 = new Person("lili");
p2.getName(); //lili
p2.setName("mat"); //mat
p1.getName(); //zzy
可以看到构造函数法,每个实例都是独立的。对应着各自的私有变量值。
私有变量和私有函数是由实例共享的。因为特权方法定义在原型上,所以同样是由实例共享的。
(function() {
let name = '';
Person = function(value) { //不使用var let等关键字变量声明,直接变成全局变量
name = value;
};
Person.prototype.getName = function() {
return name;
}
Person.prototype.setName = function(value) {
name = value;
}
})();
let p1 = new Person("zzy");
p1.getName(); //zzy
let p2 = new Person();
p2.getName(); //zzy
p2.setName("lili");
p1.getName(); //lili
函数
函数是实际上是对象。每个函数都是Function类型的实例,而 Function 也有属性和方法,跟其他引用类型一样。因为函数是对象,所以函数名就是指向函数对象的指针。
定义函数
采用function关键字定义函数
函数声明
函数表达式
箭头函数
使用事项:
箭头函数也可以不用大括号,但这样会改变函数的行为。
函数事项
函数名
因为函数名就是指向函数的指针,所以它们跟其他包含对象指针的变量具有相同的行为。
多个名称
name属性
ECMAScript 6 的所有函数对象都会暴露一个只读的 name 属性,其中包含关于函数的信息。多数情
况下,这个属性中保存的就是一个函数标识符,或者说是一个字符串化的变量名。即使函数没有名称,
也会如实显示成空字符串。如果它是使用 Function 构造函数创建的,则会标识成"anonymous":
如果函数是一个获取函数、设置函数,或者使用 bind()实例化,那么标识符前面会加上一个前缀:
参数
ECMAScript 函数的参数跟大多数其他语言不同。ECMAScript 函数既不关心传入的参数个数,也不关心这些参数的数据类型。定义函数时要接收两个参数,并不意味着调用时就传两个参数。你可以传一个、三个,甚至一个也不传,解释器都不会报错。主要是因为 ECMAScript 函数的参数在内部表现为一个数组。函数被调用时总会接收一个数组,但函数并不关心这个数组中包含什么。
在使用 function 关键字定义(非箭头)函数时,可以在函数内部访问 arguments 对象(箭头函数没有该对象),从中取得传进来的每个参数值。
arguments 对象是一个类数组对象(但不是 Array 的实例),因此可以使用中括号语法访问其中的元素(第一个参数是 arguments[0],第二个参数是 arguments[1])。而要确定传进来多少个参数,可以访问 arguments.length 属性。
注意:箭头语法定义的函数,将不能使用 arguments 关键字访问参数,而只能通过定义的命名参数访问。
没有重载
ECMAScript 函数没有签名,因为参数是由包含零个或多个值的数组表示的。没有函数签名(接收参数的类型和数量),自然也就没有重载。如果在 ECMAScript 中定义了两个同名函数,则后定义的会覆盖先定义的。
默认参数值
注意:修改命名参数也不会影响 arguments 对象,它始终以调用函数时传入的值为准
默认参数作用域与暂时性死区
给多个参数定义默认值实际上跟使用 let 关键字顺序声明变量一样。
参数是按顺序初始化的,所以后定义默认值的参数可以引用先定义的参数。
参数初始化顺序遵循“暂时性死区”规则,即前面定义的参数不能引用后面定义的。
参数也存在于自己的作用域中,它们不能引用函数体的作用域
函数声明与函数表达式
函数声明会在任何代码执行之前先被读取并添加到执行上下文。这个过程叫作函数声明提升。
函数表达式
函数定义包含在一个变量初始化语句中,而不是函数声明中。这意味着代码如果没有执行到加粗的那一行,那么执行上下文中就没有函数的定义。换成var也是存在错误,不过由于var的变量提升错误类型不一样了。sum能找到,但是是
undefined
,因此会是TypeError
。注意:
ReferenceError
就是在作用域中找不到、TypeError
是在作用域中找到了但是 做了它不可能做的事情。函数内部
arguments
arguments对象只有以 function 关键字定义函数(相对于使用箭头语法创建函数)时才会有。虽然主要用于包含函数参数,但 arguments 对象其实还有一个 callee 属性,是一个指向 arguments 对象所在函数的指针。
this
它在标准函数和箭头函数中有不同的行为。在标准函数中,this 引用的是把函数当成方法调用的上下文对象,这时候通常称其为 this 值(在网页的全局上下文中调用函数时,this 指向 windows)。
1.标准函数this指向:
因为
o.sayColor
函数中的this对象是o,沿着o的作用域链(原型链,注意此处o是对象访问不到window对象上的color)先找到的是o实例上的color属性。2.箭头函数this指向:
在箭头函数中,this引用的是定义箭头函数的上下文。下面的例子演示了这一点。在对
sayColor()
的两次调用中,this 引用的都是 window 对象,因为这个箭头函数是在 window 上下文中定义的:🌈箭头函数妙用:
在事件回调或定时回调中调用某个函数时,this 值指向的并非想要的对象。此时将回调函数写成箭头函数就可以解决问题。这是因为箭头函数中的 this 会保留定义该函数时的上下文:
此处箭头函数this指向定义时的上下文,
也就保留着作用域链也就是King函数内部的属性和方法。前面作用域链删除是因为这个说法验证有问题,如果this指向定义时的上下文,那么这个King的上下文中的作用域链应该是保存了外部属性的,但其实this却访问不了,如下:可以看出this对象是King。所以能访问的内容也只有King对象实例的属性和方法。如果把
this.royaltyName = 'Henry'
改成var royaltyName = 'Henry'
,setTimeout
函数的this是访问不到royaltyName
变量,因为这不是King的实例属性。同样如果调用King的时候不采用new关键字,this也是指向window对象。也就是说箭头函数this指向定义时的上下文就只是保留了箭头函数外部函数对象的属性和方法。如下,只能访问到F1内部。
但是如果不采用this.age其实可以访问到,函数内部沿着作用域链可以找到age:
caller
这个属性引用的是调用当前函数的函数,或者如果是在全局作用域中调用的则为 null。
ECMAScript 6 新增了检测函数是否使用 new 关键字调用的 new.target 属性。如果函数是正常调用的,则 new.target 的值是 undefined;如果是使用 new 关键字调用的,则 new.target 将引用被调用的构造函数。
闭包
闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。
全局上下文中的叫变量对象,它会在代码执行期间始终存在。而函数局部上下文中的叫活动对象,只在函数执行期间存在。
函数内部的代码在访问变量时,会从作用域链中查找变量。函数执行完毕后,局部活动对象会被销毁,内存中就只剩下全局作用域。不过,闭包就不一样了。
在一个函数内部定义的函数会把其包含函数的活动对象添加到自己的作用域链中。因此,在
createComparisonFunction()
函数中,匿名函数的作用域链中实际上包含createComparisonFunction()
的活动对象。如下代码:
不过上面的图存在一个问题,
createComparisonFunction
函数作用域中是访问不到result
因为result
在函数调用之后才声明。存疑:
在前面的this时,this引用的是定义箭头函数的上下文
然而
按理来说King这里应该也是指向window呀。但是后面反思了一下,这个箭头函数定义是在函数King内部。可能函数会产生新的上下文也就是King函数内部上下文,而对于对象来说则不会产生新的上下文,对象的方法属性还是在全局上下文定义的。
由于函数
getIdentityFunc
是object调用的,但是内部的return的函数不是,所以对象就是window。object调用函数getIdentityFunc
,因此它内部的this指向object。要想在内部return的函数使用这个this也很简单,利用闭包的特性,getIdentityFunc
函数的that变量。总之: