谈谈Javascript中的变量升级
字数 1075 2025-08-20 18:17:47
JavaScript中的变量升级与作用域解析
1. Hoisting(变量提升)
JavaScript中的变量提升(Hoisting)是一个重要的概念,它指的是变量和函数的声明会在编译阶段被提升到当前作用域的顶部。
1.1 基本示例
var x = 1;
console.log(x + " " + y); // '1 undefined'
catName("Chloe") //'Choloe'
var y = 2;
function catName(name) {
console.log("我的猫名叫 " + name);
}
等效代码:
var x=1;
var y;
console.log(x + " " + y); // '1 undefined'
function catName(name) {
console.log("我的猫名叫 " + name);
}
y = 2;
catName("Chloe") //'Choloe'
1.2 函数内部的变量提升
var username = 'hpdoger';
function echoName(){
console.log(username); //undefined
var username = 'wuyanzu';
}
echoName();
关键点:
- 变量和函数声明会在编译阶段被处理
- 只有声明被提升,赋值操作不会被提升
- 函数声明比变量声明优先级更高
2. 变量升级
变量升级指的是变量作用域意外提升的情况,可能导致安全问题。
2.1 声明错误导致的变量升级
function echoName(){
var username = 'hpdoger'
nickname = 'wuyanzu'; // 没有使用var/let/const声明,自动成为全局变量
}
function CheckVal(){
console.log(nickname); //wuyanzu
console.log(username); //Uncaught ReferenceError: username is not defined
}
echoName();
CheckVal();
2.2 案例1-Fake Protect
<html>
<body>
<script>
const whiteList = ['index.html','404.html','hpdoger.html'];
function init(){
const Content = new Object();
Content["title"] = "XSS Demo";
page = location.hash.slice(1); // 没有声明page变量,成为全局变量
if(!whiteList.includes(page)){
Content["page"] = "404.html";
}else{
window.page = page;
}
return Content;
}
function loadPage(page){
window.open(page);
}
let Content = init();
alert(Content["title"]);
page?loadPage(page):loadPage(Content.page); // 可以直接控制page变量
</script>
</body>
</html>
安全问题:未声明的page变量成为全局变量,绕过白名单检查。
2.3 案例2-midnightCTF(Crossintheroof)
<?php
$xss = $_GET['xss']?$_GET['xss']:"";
$xss = preg_replace("|[}/{]|", "", $xss);
?>
<script>
setTimeout(function(){
try{
return location = '/?i_said_no_xss_4_u_:)';
nodice=<?php echo $xss; ?>;
}catch(err){
return location = '/?error='+<?php echo $xss; ?>;
}
},500);
</script>
解决方案:
xss=alert(1);%0a+const+location=1;
原理:
- 使用
const声明局部变量location,覆盖全局的window.location - 赋值操作会产生runtime error,进入catch块
- 分号分隔语句,确保
alert(1)先执行
2.4 for循环中的变量升级
names = ['55kai','pdd','dasima'];
for (var i=0;i<names.length;i++) {
if(names[i] === 'dasima'){
console.log('wuhu~');
}else{
console.log(names[i]);
}
}
console.log(i); // 3 - i被提升到函数作用域
关键点:
var声明的变量会提升到当前函数作用域- 未使用
var声明的变量会成为全局变量
2.5 with表达式中的变量升级
function getUrlParam(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]); return null;
}
function init(Content,values='index.html'){
with (Content) {
title = 'XSS Demo';
page = values;
location = values; // 如果Content没有location属性,会升级为全局变量
}
}
let values = location.hash.slice(1);
let uid = getUrlParam("role");
let ContentAdmin = {
title:'',
page:''
}
let ContentUser = {
title:'',
location:''
}
if(uid!=="admin"){
init(ContentUser,values);
}else{
init(ContentAdmin,values);
}
安全问题:
- 当
Content对象没有location属性时,location会被升级为全局变量 - 可以修改
window.location实现XSS
2.6 this绑定导致的变量升级
var myobj = {
getbar : function(){
console.log(this.bar);
},
bar: 1
};
var bar = 2;
var getbar = myobj.getbar;
myobj.getbar() //1 - this指向myobj
getbar(); //2 - this指向全局对象(浏览器中是window)
关键点:
- 函数中的
this默认绑定到调用它的对象 - 没有明确绑定时,
this会指向全局对象
3. 防御措施
- 始终使用严格模式:
'use strict'可以防止意外创建全局变量 - 使用let/const代替var:块级作用域可以避免变量提升问题
- 避免使用with语句:with会改变作用域链,容易导致变量升级
- 明确绑定this:使用
.bind(),.call(),.apply()或箭头函数明确this指向 - 代码检查工具:使用ESLint等工具检查未声明的变量
4. 总结
JavaScript中的变量作用域管理需要特别注意:
- 变量提升(Hoisting)是语言特性,而变量升级是潜在的安全问题
- 未正确声明的变量会升级为全局变量,可能导致安全漏洞
- with语句和this绑定是常见的变量升级来源
- ES6的let/const可以解决大部分作用域问题
- 严格模式是防止意外全局变量的有效手段
开发者应养成良好的变量声明习惯,避免因变量升级导致的安全问题。