返回

从变量到词法环境:初探JS

前端

变量

变量是用来存储数据的容器。它们可以是任何类型的数据,包括字符串、数字、布尔值、数组和对象。变量用var、let或const声明,后面跟着变量名和一个等于号 (=)。例如:

var name = "John Doe";
let age = 30;
const PI = 3.14;

词法环境

词法环境是一个存储变量的容器。它与函数相关联,并且在函数执行时创建。词法环境包含函数的参数、局部变量和函数声明的变量。当函数执行时,词法环境链被创建,并且该链包含当前词法环境及其父词法环境。

作用域

作用域是变量可以被访问的区域。变量的作用域由词法环境决定。全局变量的作用域是整个程序,局部变量的作用域是它所在的函数。例如:

function sayHello() {
  var name = "John Doe";
  console.log(name); // "John Doe"
}

sayHello();

console.log(name); // ReferenceError: name is not defined

在上面的示例中,变量name的作用域是函数sayHello()。当函数sayHello()执行时,词法环境链被创建,并且该链包含sayHello()的词法环境和全局词法环境。当console.log(name)在sayHello()中执行时,它使用sayHello()的词法环境来查找name变量。然而,当console.log(name)在sayHello()之外执行时,它使用全局词法环境来查找name变量,但name变量在全局词法环境中不存在,所以会抛出一个ReferenceError异常。

闭包

闭包是一个可以访问其创建函数之外的变量的函数。闭包在JavaScript中很常见,并且通常用于创建私有变量和方法。例如:

function createCounter() {
  var count = 0;

  return function() {
    count++;
    return count;
  };
}

var counter = createCounter();

console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

在上面的示例中,函数createCounter()返回一个闭包。该闭包可以访问其创建函数之外的变量count。当闭包被调用时,它使用createCounter()的词法环境来查找count变量。因此,当console.log(counter())执行时,它使用createCounter()的词法环境来查找count变量,并且count变量的值被递增并返回。

原型

每个JavaScript对象都有一个原型对象。原型对象是该对象继承属性和方法的对象。例如:

var obj = {
  name: "John Doe",
  age: 30
};

console.log(obj.hasOwnProperty("name")); // true
console.log(obj.hasOwnProperty("age")); // true
console.log(obj.hasOwnProperty("toString")); // false

在上面的示例中,obj对象有一个name和age属性。它还继承了toString()方法,该方法来自Object原型对象。因此,当console.log(obj.hasOwnProperty("name"))执行时,它返回true,因为obj对象拥有name属性。当console.log(obj.hasOwnProperty("age"))执行时,它也返回true,因为obj对象拥有age属性。然而,当console.log(obj.hasOwnProperty("toString"))执行时,它返回false,因为obj对象不拥有toString()方法。相反,它继承了toString()方法,该方法来自Object原型对象。

原型链

原型链是JavaScript对象之间的连接链。它用于查找对象的属性和方法。当对象访问一个属性或方法时,它首先在对象本身中查找。如果属性或方法在对象中找不到,则在对象的原型对象中查找。如果属性或方法在对象的原型对象中找不到,则在对象的原型对象的原型对象中查找,依此类推。例如:

var obj = {
  name: "John Doe",
  age: 30
};

console.log(obj.name); // "John Doe"
console.log(obj.age); // 30
console.log(obj.toString()); // "[object Object]"

在上面的示例中,obj对象有一个name和age属性。它还继承了toString()方法,该方法来自Object原型对象。因此,当console.log(obj.name)执行时,它返回"John Doe",因为name属性在obj对象中。当console.log(obj.age)执行时,它也返回30,因为age属性在obj对象中。然而,当console.log(obj.toString())执行时,它返回"[object Object]",因为toString()方法不在obj对象中,而是继承自Object原型对象。