【附录A—高级特性】《node.js开发指南》读书笔记

本文深入探讨JavaScript的作用域规则,包括函数作用域、词法作用域和闭包的概念。解释了闭包如何允许函数访问并操作外部作用域中的变量,以及如何利用闭包实现私有变量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A.1 作用域

 作用域(scope)决定变量的可见范围和生命周期。

 

A1.1 函数作用域

在函数引用一个变量时,js会优先搜索当前作用域。如果没有找到则搜索上层作用域,直到顶层。

可是以下代码会带来困惑:

var scope = 'global';
var f = function () {
  console.log(scope); // 输出 undefined
  var scope = 'f';
}
f();

 因为在f函数内已经定义scope,但是他没有被初始化。所以输出undefined。

 

js使用静态作用域(词法作用域)。如下列代码,foo()仍然不会动态的使用a=3。

var a = 11;
function foo() {
  console.log(a);
}
function bar() {
  var a = 3;
  foo();
}

bar(); // 2;

 

A.2 闭包

闭包(closure)是函数式编程中的概念。

 

A2.1 什么是闭包?

var generateClosure = function () {
  var count = 0;
  var get = function () {
    count++;
    return count;
  };
  return get;
};

var counter = generateClosure();
console.log(counter()); // 输出 1
console.log(counter()); // 输出 2
console.log(counter()); // 输出 3

按照命令式编程思维的理解,count是函数内部的变量,他的生命周期只是在函数运行时。当函数从调用栈返回时,count变量申请的空间就被释放。

不过因为闭包特性,函数内部的get函数被一个外部变量counter时,就产生了一个闭包。

因为get访问了count,所以他没有被回收。

 

而且闭包实例之间是不共享内存的,比如:

var generateClosure = function () {
  var count = 0;
  var get = function () {
    count++;
    return count;
  };
  return get;
};
var counter1 = generateClosure();
var counter2 = generateClosure();
console.log(counter1()); // 输出 1
console.log(counter2()); // 输出 1
console.log(counter1()); // 输出 2
console.log(counter1()); // 输出 3
console.log(counter2()); // 输出 2

 

A.2.2 闭包的用途

2.实现私有成员

js没有私有属性,也就说每一个属性都是暴露给外部的,这样是不合理的。它会导致开发者能够直接函数修改属性值,破坏一致性。

通过闭包可以实现私有变量,还使用上面的代码作为演示:

var generateClosure = function () {
  var count = 0;
  var get = function () {
    count++;
    return count;
  };
  return get;
};
var counter = generateClosure();
console.log(counter()); // 输出 1
console.log(counter()); // 输出 2
console.log(counter()); // 输出 3

 其中只有特权函数get可以访问到count变量。私有属性便形成了。

 

A.3 对象

有别于其他的面向对象语言,js的对象是基于原型的。

 

A.3.1 创建和访问

js可以这样创建一个简单的对象:

var foo = {};

foo.prop_1 = 'bar';
foo.prop_2 = false;


foo.prop_3 = function(){
  return this.prop_1;
}

console.log(foo.prop_3());

而且以下两种方式是等价的。

var foo1 = {};
var foo2 = new Object();

 

A.3.2 构造函数

 创建对象只是一次性的,我们需要多个对象,有属性,方法,并且能够初始化。

js提供了构造函数:

function User(name,uri){
  this.name = name;
  this.uri = uri;
  this.display = function(){
    console.log(this.name);
  }
}

var someuser = new User('v','http://www.byvoid.com');

 

A.3.3 上下文属性

在js中,上下文对象就是this指针,即被函数调用的环境。

下列代码中,随着使用位置的不同,this也随之变化,所以输出也不尽相同。

var someuser = {
  name: 'byvoid',
  func: function () {
    console.log(this.name);
  }
};

var foo = {
  name: 'foobar'
};
someuser.func(); // 输出 byvoid

foo.func = someuser.func;
foo.func(); // 输出 foobar

name = 'global';
func = someuser.func;
func(); // 输出 global

 

A.3.4 原型

构造函数和对象区别:

1.构造函数内定义的属性继承方式与原型不同,子对象需要显式调用父对象才能继承构造函数内定义的属性。

2.构造函数内定义的任何属性,包括函数在内都会被重复创建,同一个构造函数产生的两个对象不共享实例。

但是原型链却是共享的,比如:

下列代码使用a2修改了a1原型链name的值。

function A(){
  this.setName = function(newName){
    A.prototype.name = newName;
  }
}

A.prototype.name = "Sophie Dupont";

let a1 = new A();
let a2 = new A();

console.log(a1.name); //Sophie Dupont
a2.setName('Robert');
console.log(a1.name); //Robert

 3.构造函数内定义的函数有运行时闭包的开销,因为构造函数内的局部变量对其中定义的函数来说也是可见的。

 

 

原型链部分的内容介绍的比较粗浅,便不写入笔记了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值