由for in引起的深思-离谱的歪门邪道

发布于 2023-09-04  665 次阅读


内容纲要

JavaScript中对象的属性值可以使用.来获取,也可使用[]包裹属性名来获取。

一般来说两种方法都适用于直接获取属性值。

但是如果是使用变量作为属性值,或者是通过for in 来遍历对象,使用.来得到属性值就行不通了。

例如,定义一个对象:

let user={
        name:"ProKingDU",
        pass:"**************",
        age:19,
        sex:"雄",
    }

简单输出一下某一个属性的值:

console.log(user.name);
console.log(user['name']);

很明显,使用.来获取属性值直接跟随属性名就可以,但是如果是[]就需要使用字符串包裹属性名。

所以如果是通过变量来获取属性:

    console.log(user[name]);
    let name='pass';
    console.log(user.name);
    console.log(user[name]);

这段代码很有意思,第一个打印输出会报出一下错误:

load.html?_ijt=vegm8pr379k771cgojn6td2700:44 Uncaught ReferenceError: Cannot access 'name' before initialization
    at load.html?_ijt=vegm8pr379k771cgojn6td2700:44:22
(anonymous) @ load.html?_ijt=vegm8pr379k771cgojn6td2700:44

如果注释第一句,则变量下面两段代码都是正常执行,但是值不相同,第一句输出name属性的值,第二句才输出pass属性的值。

所以得出一条教训:

尽量不要让命名重复,无论是属性还是变量,亦或者方法名,这可能产生意想不到的离谱错误!

因为在作用域内不允许在一个变量声明之前就调用它,如果你没有声明这个变量,并且在某处使用了他,会产生两种情况:

1.报错

Uncaught ReferenceError: aaaa is not defined
    at load.html?_ijt=vegm8pr379k771cgojn6td2700:51:17

2.得到undefined

例如:

console.log(test);
console.log(test+1);

这时候变量test并不存在,所以会产生错误。

console.log(user.myvar);
console.log(typeof myvar);

这两种是特殊情况,都得到undefined,第一句是因为:这里获取对象属性没有使用myvar这个变量,他本来就不存在,且对象没有myvar这个属性,所以得到undefined,而第二句,一个不存在的变量,或者被声明但是没有赋值的变量都是undefined。不存在的变量没有值,且类型为undefined,被声明没有赋值的变量,值和类型都是undefined。

因为第一种情况与变量无关,所以可以得出:

当变量没有被赋值或者定义时默认的类型是undefined。

然后接着尝试遍历以上对象:

    for (const key in user) {
        // console.log(user.key)
    }

很明显,这是一种错误的做法,因为上面已经解释了,通过.调用对象的属性是直接使用属性名,而不会选择对应的变量。

这段代码不会报错,但是会得到undefined。因为在user对象中key属性不存在。

接下来:

    for (const key in user) {
        console.log("属性:"+key+",值:"+user[key]);
    }

这段代码就可以很好的输出对象的所有键值对,因为Key是一个变量,储存对象当前的属性,实际上就是一个字符串,而通过数组风格来访问属性值,所需要的键也是一个字符串形式,所以能够得到属性值。

那么我非要使用.来遍历应该怎么做?

首先改写一下上面的输出吧!

console.log(`属性:${key},值:${user[key]}`);

没错,通过``和${}实现格式化字符串。

我尝试这样:

    for (const key in user) {
                console.log(`user.${key}`);

    }

却意外的得到了:

于是:如果将上面的四个结果直接写进代码里面,就能成功得到属性的值了啊!

立马想到eval()!

    for (const key in user) {
        console.log(eval(`user.${key}`))
    }

妙啊!!!!!!!

同样可以获取到值并且保存到变量里面:

for (const key in user) {
        key === 'pass' ? pass=eval(`user.${key}`) : key;
    }
console.log(pass);

在扩展一下思路:
在PHP中,可以通过变量名作为函数名引用来实现动态调用函数。

例如:

$name=$_POST['name'};
$name();

以上例子是一个简单的通过POST请求调用函数的代码。

而在js或者Python中,一切皆对象,这样做明显不可能。

python:

def add(a, b):
    return a + b


name = "add"
name(1.5)

js:

    let a="test";
    function test(){
        console.log('success');
    }
    a();

分别报错:

Traceback (most recent call last):
  File "D:\项目代码\Pycode\GPT\main.py", line 15, in <module>
    name(1.5)
TypeError: 'str' object is not callable
load.html?_ijt=vegm8pr379k771cgojn6td2700:69 Uncaught TypeError: a is not a function
    at load.html?_ijt=vegm8pr379k771cgojn6td2700:69:5

php:

function test(){
    echo 'ssuccess';
}
$a='test';
$a();

结果:

虽然js和python有闭包和匿名函数来将函数封装到变量内,但我就是不想这样做,并且我还要能够通过变量动态的调用不同的函数。

仍然可以通过eval完成。

js:

    let a="test";
    function test(){
        console.log('success');
    }
    function sec(){
        console.log('ok')
    }
    eval(`${a}()`);
    a='sec';
    eval(`${a}()`);
    

结果:

python:

def add(a, b):
    return a + b


name = "add"


def floor(a, b):
    return int(a / b)


print(eval("%s(1,3)" % name))
name="floor"
print(eval("%s(5,2)" % name))

当然,这样也就自己玩个乐呵,eval很多情况下都是被禁用的函数,因为如果对外业务会存在很严重的注入漏洞,比sql注入还严重的那种!!!!!

如果是在要用,一定要前后写好变量内容过滤!

结果: