1、定义一个函数,JavaScript内部各做了哪些事情
定义一个函数时,JavaScript内部执行了以下步骤:
-
解析函数声明 :
当你定义一个函数时,JavaScript的解析器会首先解析函数声明。这意味着它会检查函数声明的语法是否正确,包括函数名、参数列表、函数体等。
-
创建函数对象 :
一旦函数声明被解析通过,JavaScript会在内存中创建一个函数对象。这个函数对象包含了函数的定义、参数信息、函数体以及其它与函数相关的元数据。
-
函数作用域和闭包 :
在函数创建的过程中,JavaScript会确定函数的作用域。这包括确定函数内部可以访问的变量和函数。如果函数内部引用了外部作用域的变量,那么这些变量会被"封闭"在函数内部,形成闭包。闭包允许函数在执行完毕后依然能够访问其定义时的词法环境。
-
将函数对象赋值给变量 :
如果你使用了变量来定义函数(例如
var myFunction = function() { ... };
),那么JavaScript会将新创建的函数对象赋值给相应的变量。这样,你就可以通过变量来引用和调用这个函数了。 -
函数原型和
prototype
属性 :每个函数对象都有一个
prototype
属性,它指向一个原型对象。这个原型对象包含了可以被函数的所有实例共享的属性和方法。当你使用new
关键字创建函数的新实例时,新实例的内部[[Prototype]]
链接会指向这个原型对象,从而可以访问原型上的属性和方法。
函数对象的原型对象(prototype)的默认值是一个空的Object对象。
换句话说,当你定义一个函数时,JavaScript会自动为其添加一个prototype属性,这个属性的默认值是一个空的对象(即不包含任何属性和方法的对象)。这个原型对象主要用于实现基于原型的继承和属性查找。
在JavaScript中,每个构造函数都有一个prototype属性,这个属性是一个指针,指向一个对象,该对象的用途是包含可以由特定类型的所有实例共享的属性和方法。按照惯例,这个prototype对象会包含一个名为constructor的属性,该属性是一个指向prototype属性所在函数的指针。这样,构造函数就能够识别哪些对象是其实例。
需要注意的是,虽然prototype对象的默认值是一个空对象,但你可以根据需要向其中添加属性和方法,以便在构造函数的实例中共享这些属性和方法。此外,你也可以通过修改prototype对象来修改已有实例的行为,但这通常是不推荐的,因为这可能会导致代码难以理解和维护。
下面是一个简单的例子,展示了如何查看一个函数对象的prototype属性的默认值:
javascriptfunction MyFunction() { // 函数体 } console.log(MyFunction.prototype); // 输出: {}
在这个例子中,
MyFunction
是一个空函数,我们打印出它的prototype属性,可以看到它的默认值是一个空的Object对象。
- 函数属性和方法 :
函数对象自身也有一些属性和方法,例如name
(函数名)、length
(参数个数)、call
、apply
、bind
等。这些属性和方法允许你操作函数或获取函数的元数据信息。
下面是一个简单的函数定义示例:
javascript
function myFunction(arg1, arg2) {
// 函数体
console.log('Hello, world!');
}
在这个例子中,JavaScript会执行以下步骤:
- 解析函数声明
myFunction
,检查其语法是否正确。 - 创建一个新的函数对象,包含
myFunction
的定义、参数arg1
和arg2
、函数体以及其它元数据。 - 确定函数的作用域,并检查是否有闭包形成。
- 将这个函数对象赋值给变量
myFunction
。 - 为这个函数对象创建一个
prototype
属性,并设置其默认的constructor
属性指向函数对象本身。
最终,你可以通过myFunction
变量来调用这个函数,并执行其函数体中的代码。
2、new 创建一个函数实例时,JavaScript内部做了哪些事情
当使用 new
关键字创建一个函数的实例时,JavaScript 内部执行以下步骤:
-
创建新对象 :
JavaScript 首先会创建一个新的空对象。这个新对象将用作新创建的实例。
-
设置原型链 :
新创建的对象的内部
[[Prototype]]
链接(这不是一个真正的属性,而是一个内部链接)会被设置为构造函数的prototype
对象。这意味着新创建的对象可以继承构造函数原型上的属性和方法。 -
构造函数调用 :
接下来,构造函数会被调用,并将新创建的对象作为
this
上下文。这意味着在构造函数内部,你可以使用this
关键字来访问和修改新创建的对象。 -
执行构造函数体 :
构造函数的代码(函数体)会被执行。在这个过程中,你可以定义新对象的属性、方法,以及执行其他任何初始化代码。
-
返回新对象 :
如果构造函数没有显式地返回一个非原始值(即非
null
、undefined
、数字、字符串或布尔值),那么new
表达式的结果就是新创建的对象。如果构造函数返回了一个对象,那么这个返回的对象将替代新创建的对象,并作为new
表达式的结果。 -
实例属性和方法 :
在构造函数中定义的任何属性和方法都将成为新创建对象的实例属性和方法。这些属性和方法仅对当前实例可见,每个实例都会有自己的一套属性和方法的副本。
下面是一个使用 new
关键字创建函数实例的示例:
javascript
function Person(name, age) {
this.name = name;
this.age = age;
this.introduce = function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
var john = new Person('John', 30);
john.introduce(); // 输出: Hello, my name is John and I am 30 years old.
在这个例子中:
Person
是一个构造函数。- 使用
new Person('John', 30)
创建了一个新的Person
实例。 - 新对象的
[[Prototype]]
链接被设置为Person.prototype
。 - 构造函数被调用,并设置了新对象的
name
和age
属性,以及introduce
方法。 introduce
方法是定义在新对象上的实例方法,每个实例都会有自己的方法副本。john
变量引用了新创建的Person
实例,并可以调用其introduce
方法。