JavaScript变量与函数核心机制解析

作者:小括号时间:2026-10-26 13:18:22浏览:144

本文深入解析JavaScript中变量存储机制,区分原始值与引用值的栈堆分配差异。详细阐述函数作为对象的特性,解析this关键字的动态绑定规则及call/apply用法。通过代码示例说明作用域、闭包概念及解决循环绑定问题的技巧,帮助开发者掌握JS核心概念。

一、变量

JavaScript变量存储机制

JavaScript中的变量主要存储两类数据:原始值(Primitive Values)和引用值(Reference Values)。


原始值直接存储在栈内存(Stack)的简单字段中,变量的值即数据本身,直接占据变量所指向的内存位置。


引用值则存储在堆内存(Heap)中,栈内存里的变量仅保存指向堆内存中实际对象的指针地址。


JavaScript定义了5种基本(原始)类型:Undefined、Null、Boolean、Number和String。


引用类型本质上就是对象,其概念类似于其他面向对象语言中的类实例。


具体存储示例如下:

 
var b = true; // 存储在栈上
var num = 20; // 存储在栈上
var b = new Boolen(true); // 存储在堆内
var num = new Number(20); // 存储在堆内

生成对象通常有以下几种方式:


使用new关键字加类型名称,例如:var obj = new object()。


如果构造函数不需要参数,可以省略括号,写作:var obj = new object。


此外,还可以使用对象字面量语法快速创建对象:var obj = {}。


二、函数

函数特性与上下文管理

在JavaScript中,函数也是一种对象。因此,应当像对待其他对象一样对待函数,每个函数对象都隐式附加了两个额外参数:this和arguments。


函数具有多种用途:可以赋值给变量,作为其他对象的属性,作为参数传递给其他函数,作为返回值返回,也可以使用函数字面量来创建。


关于函数上下文(Context):


在传统的面向对象语言中,this关键字用于引用当前类的实例。然而,JavaScript中的this行为不同,因为函数即对象,这里的this引用的是调用当前函数的那个上下文对象。


可以通过call()和apply()方法显式地指定函数的执行上下文。call()方法的第一个参数被用作调用函数时的上下文对象,其余参数则作为实参传递给被调用函数。apply()方法与call()类似,区别在于第二个参数必须是一个数组(或类数组对象)。


上下文切换的代码示例如下:

 
var obj = {
m:"hello"
} 
var m="hi";
var say=function()
{ 
alert(this.m); //this指向函数调用上下文
} 
say();//hi,window为调用上下文
say.call(obj);// hello,此时obj为函数调用上下文
say.call(window);// hi,window为调用上下文

作用域与闭包原理

关于作用域(Scope):


定义在函数内部的参数和变量在函数外部是不可见的;反之,在一个函数内部任何位置定义的变量,在该函数内部的任何地方都是可见的。


作用域示例代码如下:

 
var obj = function() {
var num = 1;
return { getValue: function() {
alert(num); //undefined
var num = 2;
alert(num); //2
} 
} 
} (); 
obj.getValue();

关于闭包(Closure):


所谓闭包,是指一个函数能够访问并记住其被创建时所处的词法环境,即函数可以使用定义在其外部的变量,访问创建时的上下文环境。


闭包示例代码如下:

 
var hello = "hello word!";
function sayHello() {
alert(hello);
} 
sayHello();
var obj = function() {
var value = 0;
return {
setValue: function(val) {
value += typeof val === 'number' ? val : 1;
}, 
getValue: function() {
return value;
} 
} 
} (); 
obj.setValue('a');
alert(obj.getValue()); //1

注意最后一行的(),()是调用运算符,它表示函数立即调用并返回调用结果。因此,obj引用的是该函数返回的一个包含两个方法的对象,而非函数本身,且这两个方法享有访问value变量的特权。


再举一个常见的内部函数访问外部函数局部变量的例子,用于说明闭包的应用:点击相应的列表项时弹出对应的顺序编号。


相关HTML结构如下:

 
<li id="a">test1</li>
<li id="b">test2</li>
<li id="c">test3</li>
var test = function() {
var num = document.getElementsByTagName("li");
var i;
for (i = 0; i < num.length; i++) {
num[i].onclick = function() {
alert(i); //内部函数可以访问外部函数变量,i的最终值是3,
//而不是构造函数时的i值,所以此处都弹出3
} 
} 
alert(i); //3
} 
test();

下面这个写法可以获得正确的结果:


修正后的代码如下:

 
var test = function() {
var num = document.getElementsByTagName("li");
var i;
for (i = 0; i < num.length; i++) {
num[i].onclick = function(i) {
return function() {
alert(i + 1);
} 
} (i); //每次构造函数时立即把i值传进去执行,现在onclick绑定的函数是执
//行后返回的函数
} 
alert(i); //3
} 
test();
作者最新文章