gpt4 book ai didi

javascript - JavaScript声明一个变量并在一个语句中使用逗号运算符?

转载 作者:行者123 更新时间:2023-12-01 16:04:26 25 4
gpt4 key购买 nike

众所周知,要声明多个变量,可以使用如下格式:

let k = 0,
j = 5 /*etc....*/

还众所周知,要在一行中执行多个语句(这对于箭头功能很有用,因此不必编写 return关键字),还使用逗号“,”运算符,如下所示:

let r = "hello there world, how are you?"
.split("")
.map(x => (x+=5000, x.split("").map(
y => y+ + 8
).join("")))
.join("")

console.log(r)


不是最优雅的示例,但要点是您可以在一行中执行多个语句,并用逗号“,”分隔,然后返回最后一个值。

所以问题是:

您如何结合这两种技术?意思是,我们如何在一行中声明一个变量,然后在逗号后面用该变量做某事?

以下内容不起作用:
let k = 0, console.log(k), k += 8



Uncaught SyntaxError: Unexpected token '.'



并且没有console.log,它认为我在重新声明k:
let k = 0, k += 8


Uncaught SyntaxError: Identifier 'k' has already been declared

然后将整个内容放在括号中,如下所示:
(let k = 0, k += 8);


Uncaught SyntaxError: Unexpected identifier

指关键字“let”。但是,没有该关键字就没有问题:
(k = 0, k += 8);

除了k现在变成不需要的全局变量的事实之外。

这里有什么解决方法吗?

如何在JavaScript中将逗号运算符与局部变量声明一起使用?

编辑以响应VLAZ的评估部分,以将参数传递到评估中,可以创建自定义函数:

function meval(mainStr, argList) {
let ID = (
Math.random().toString() +
performance.now().toString()

).split(".").join("").split("")
.map(x => ("qwertyuio")[x])
.join(""),
varName = "$______"+ID+"_____$",
str = `
var ${varName} = {};
(argList => {
Object.entries(argList).forEach(x => {
${varName}[x[0]] = x[1];
})

});
`;
let myEval = eval;




return (() => {

myEval(str)(argList)
myEval(`
${
Object.keys(argList).map(x =>
"let " + x + " = " + varName + "['" + x +"'];"
).join("\n")
}
${mainStr}
delete window[${varName}];
`)

})()
}

meval(`
var g = a.ko + " world!"

`, {
a: {ko: "hi"}
})
console.log(g);

最佳答案

你不能这样做。变量声明语法允许使用逗号来一次声明多个变量。每个变量也可以可选地初始化为声明的一部分,因此语法(更抽象):

(var | let | const) variable1 [= value1], variable2 [= value2], variable3 [= value3], ..., variableN [= valueN]
但是,这不是 comma operator。就像 parseInt("42", 10)中的逗号也不是逗号运算符一样-只是逗号字符在不同的上下文中具有不同的含义。
但是,真正的问题是逗号运算符可用于表达式,而变量声明是语句。
差异的简短说明:
Expressions
基本上任何会产生值的东西: 2 + 2fn()a ? b : c等。这是将要计算并产生东西的东西。
表达式可以在很多情况下嵌套: 2 + fn()( a ? ( 2 + 2 ) : ( fn() ) )(为清晰起见,每个表达式都用方括号括起来)。即使表达式不会产生不会改变任何事情的可用值-没有显式返回的函数也会产生 undefined,因此 2 + noReturnFn()将产生乱码,但它仍然是有效的表达式语法。
注释2 of 1 (在下一节中有更多说明):变量赋值是一个表达式,执行 a = 1将产生要赋值的值:

let foo;
console.log(foo = "bar")

Statements
这些不会产生值(value)。不是 undefined就是什么。示例包括 if(cond){}return resultswitch
一条语句仅有效。您不能像 if (return 7)一样嵌套它们,因为这在语法上无效。您还不能在需要表达式的地方使用语句- console.log(return 7)同样无效。
仅需注意,表达式可以用作语句。这些称为表达式语句:

console.log("the console.log call itself is an expression statement")

因此,可以在语句有效的地方使用表达式,但不能在表达式有效的地方使用语句。
,注释2之2 :变量赋值是一个表达式,但是带赋值的变量声明不是。它只是变量声明语句语法的一部分。因此,这两者是重叠的,但并不相关,只是逗号运算符和声明多个变量的方式是相似的(允许您执行多项操作)却不相关。

console.log(let foo = "bar"); //invalid - statement instead of expression

与逗号运算符的关系
现在我们知道了区别,它应该变得更容易理解。逗号运算符的形式为
exp1, exp2, exp3, ..., expN
并接受表达式,而不是语句。它一个接一个地执行它们并返回最后一个值。由于语句没有返回值,因此它们在这种情况下永远无效: (2 + 2, if(7) {})从编译器/解释器的 Angular 来看是无意义的代码,因为此处无法返回任何内容。
因此,考虑到这一点,我们不能真正将变量声明和逗号运算符混合使用。 let a = 1, a += 1不起作用,因为逗号被视为变量声明语句,并且如果我们尝试执行 ( ( let a = 1 ), ( a += 1 ) ),该语句仍然无效,因为第一部分仍然是语句,而不是表达式。
可能的解决方法
如果您确实需要在表达式上下文中产生一个变量并避免产生隐式全局变量,那么几乎没有可用的选项。让我们使用一个函数进行说明:
const fn = x => {
let k = computeValueFrom(x);
doSomething1(k);
doSomething2(k);
console.log(k);
return k;
}
因此,它是一个产生值并在少数地方使用它的函数。我们将尝试将其转换为速记语法。
IIFE
const fn = x => (k => (doSomething1(k), doSomething2(k), console.log(k), k))
(computeValueFrom(x));

fn(42);
在自己的内部声明一个新函数,该函数将 k作为参数,然后立即使用 computeValueFrom(x)的值调用该函数。如果为了清楚起见,将函数与调用分开,则会得到:
const extractedFunction = k => (
doSomething1(k),
doSomething2(k),
console.log(k),
k
);

const fn = x => extractedFunction(computeValueFrom(x));

fn(42);
因此,该函数获取 k并与逗号运算符依次使用几次。我们只调用该函数并提供 k的值。
使用参数作弊
const fn = (fn, k) => (
k = computeValueFrom(x),
doSomething1(k),
doSomething2(k),
console.log(k),
k
);

fn(42);
与以前基本相同-我们使用逗号运算符执行多个表达式。但是,这一次我们没有额外的功能,我们只是向 fn添加了额外的参数。参数是局部变量,因此在创建局部可变绑定(bind)方面,它们的行为类似于 let / var。然后,我们将其分配给该 k标识符,而不会影响全局范围。这是我们第一个表达方式,然后我们继续其余的表达方式。
即使有人调用 fn(42, "foo"),第二个参数也将被覆盖,因此实际上它与 fn仅采用单个参数的情况相同。
使用功能正常的作弊
const fn = x => { let k = computeValueFrom(x); doSomething1(k); doSomething2(k); console.log(k); return k; }

fn(42);
我撒了谎。或更确切地说,我作弊。这不是在表达式上下文中,您拥有与以前相同的一切,但是只是删除了换行符。重要的是要记住,您可以执行此操作,并用分号分隔不同的语句。它仍然是一行,并且几乎没有更长的时间。
函数组成和函数编程
const log = x => {
console.log(x);
return x;
}

const fn = compose(computeValueFrom, doSomething1, doSomething2, log)

fn(42);
这是一个巨大的话题,所以我在这里几乎不涉及任何问题。我也大大简化了事情,只是为了介绍这个概念。
那么,什么是函数编程(FP)?
它是使用函数作为基本构建块进行编程的。是的,我们已经有函数,并且使用它们来生成程序。但是,非FP程序本质上使用命令式构造“胶合”在一起的效果。因此,您期望 iffor并调用多个函数/方法来产生效果。
在FP范例中,您具有使用其他功能一起编排的功能。很多时候,这是因为您对数据操作链感兴趣。
itemsToBuy
.filter(item => item.stockAmount !== 0) // remove sold out
.map(item => item.price * item.basketAmount) // get prices
.map(price => price + 12.50) // add shipping tax
.reduce((a, b) => a + b, 0) // get the total
数组支持来自函数世界的方法,因此这是一个有效的FP示例。
什么是功能成分
现在,假设您想从上面获得可重用的函数,然后提取这两个函数:
const getPrice = item => item.price * item.basketAmount;
const addShippingTax = price => price + 12.50;
但是您实际上并不需要执行两个映射操作。我们可以将它们重写为:
const getPriceWithShippingTax = item => (item.price * item.basketAmount) + 12.50;
但是让我们尝试这样做而不直接修改功能。我们可以一个接一个地称呼它们,这将起作用:
const getPriceWithShippingTax = item => addShippingTax(getPrice(item));
现在,我们已经重用了这些功能。我们将调用 getPrice,并将结果传递给 addShippingTax。只要我们调用的下一个函数使用上一个函数的输入,此方法就起作用。但这并不是很好-如果我们要同时调用 fgh这三个函数,则需要 x => h(g(f(x)))
现在终于到了函数组合的地方。调用它们有一定的顺序,我们可以对其进行概括。

const compose = (...functions) => input => functions.reduce(
(acc, fn) => fn(acc),
input
)

const f = x => x + 1;
const g = x => x * 2;
const h = x => x + 3;

//create a new function that calls f -> g -> h
const composed = compose(f, g, h);

const x = 42

console.log(composed(x));

//call f -> g -> h directly
console.log(h(g(f(x))));

然后,我们将这些功能与另一个功能“粘合”在一起。等效于:
const composed = x => {
const temp1 = f(x);
const temp2 = g(temp1);
const temp3 = h(temp2);
return temp3;
}
但支持任何数量的功能,并且不使用临时变量。因此,我们可以归纳出许多可以有效执行相同操作的过程-从一个函数传递一些输入,获取输出并将其馈送到下一个函数,然后重复执行。
我在哪里作弊
o,男孩,告白时间:
  • 正如我所说的那样,函数组合与采用前一个函数输入的函数一起工作。因此,为了执行我在FP部分最开始的内容,然后doSomething1doSomething2需要返回它们获得的值。我已经包含了log来显示需要发生的事情-取一个值,对其进行处理,然后返回该值。我只是想提出这个概念,所以我只用了最短的代码就可以做到这一点。
  • compose可能是误称。它有所不同,但是通过许多实现,compose通过参数向后工作。因此,如果您要调用f-> g-> h,则实际上是compose(h, g, f)。这样做是有道理的-真正的版本毕竟是h(g(f(x))),因此compose是模拟的。但是,它的阅读效果不是很好。我显示的从左到右的组合通常被命名为pipe(如Ramda)或flow(如Lodash)。我认为将compose用于功能组合标题会更好,但是您阅读compose的方式起初是违反直觉的,因此我从左至右进行了翻译。
  • 函数式编程还有很多其他东西。有一些构造(类似于数组是FP构造),使您可以从某个值开始,然后使用该值调用多个函数。但是开始构图比较简单。

  • 禁止技术 eval邓,邓,邓!
    const fn2 = x => (eval(`var k = ${computeValueFrom(x)}`), doSomething1(k), doSomething2(k), console.log(k), k)

    fn(42);
    所以...我又撒了谎。您可能会想:“老兄,如果这全都是谎言,我为什么要用这个人写给我的人”。如果您正在考虑-很好,请继续考虑。不要使用它,因为它是 super 坏
    无论如何,我认为值得一提的是在其他人没有适当解释为什么它不好的情况下就加入。
    首先,正在发生的事情-使用 eval动态创建本地绑定(bind)。然后用表示绑定(bind)。这不会创建全局变量:

    const f = x => (eval(`var y =  ${x} + 1`), y);

    console.log(f(42)); // 42
    console.log(window.y); // undefined
    console.log("y" in window); // false
    console.log(y); // error

    考虑到这一点,让我们看看为什么应该避免这种情况。
    嘿,您是否注意到我使用了 var而不是 letconst?这只是您可以自己入门的第一步。使用 var的原因是,当使用 evallet进行调用时, const始终会创建一个新的词汇环境。您可以看到规格 chapter 18.2.1.1 Runtime Semantics: PerformEval。由于 letconst仅在封闭的词法环境中可用,因此您只能在 eval内部而不是外部访问它们。

    eval("const a = 1; console.log('inside eval'); console.log('a:', a)");

    console.log("outside eval");
    console.log("a: ", a); //error

    因此,作为hack,您只能使用 var,以便声明在 eval之外可用。
    但这还不是全部。您在输入 eval时必须非常小心,因为您正在生成代码。我使用数字作弊(...一如既往)。数字文字和数字值是相同的。但是,如果没有数字,则会发生以下情况:

    const f = (x) => (eval("var a = " + x), a);

    const number = f(42);
    console.log(number, typeof number); //still a number

    const numericString = f("42");
    console.log(numericString, typeof numericString); //converted to number

    const nonNumericString = f("abc"); //error
    console.log(nonNumericString, typeof nonNumericString);

    问题在于,为 numericString生成的代码是 var a = 42;-这是字符串的值。因此,它被转换。然后使用 nonNumericString会出错,因为它会生成 var a = abc,并且没有 abc变量。
    根据字符串的内容,您会得到各种各样的东西-您可能会得到相同的值,但转换为数字,可能会得到完全不同的东西,或者可能会收到SyntaxError或ReferenceError。
    如果要将字符串变量保留为字符串,则需要生成字符串文字:

    const f = (x) => (eval(`var a = "${x}"`), a);

    const numericString = f("42");
    console.log(numericString, typeof numericString); //still a string

    const nonNumericString = f("abc"); //no error
    console.log(nonNumericString, typeof nonNumericString); //a string

    const number = f(42);
    console.log(number, typeof number); //converted to string

    const undef = f(undefined);
    console.log(undef, typeof undef); //converted to string

    const nul = f(null);
    console.log(nul, typeof nul); //converted to string

    这行得通...但是您丢失了实际输入的类型- var a = "null"null不同。
    如果获得数组和对象,则更糟,因为必须序列化它们才能将它们传递给 eval。而且 JSON.stringify不会剪切它,因为它不能完美地序列化对象-例如,它将删除(或更改) undefined值,函数,并且在保留原型(prototype)或圆形结构时会失败。
    此外, eval代码无法由编译器优化,因此它比仅创建绑定(bind)要慢得多。如果您不确定会是这种情况,那么您可能没有单击规范的链接。现在就做。
    背部?确定,您是否注意到运行 eval涉及多少东西?每个规范有29个步骤,其中多个步骤引用了其他抽象操作。是的,有些是有条件的,是的,步骤的数量并不一定意味着要花费更多的时间,但是它肯定比创建绑定(bind)需要做更多的工作。提醒,引擎无法即时优化,因此您将比“真实”(非 eval ed)源代码要慢。
    在提到安全性之前。如果您不得不对代码进行安全性分析,那么您会非常讨厌 eval。是的, eval可以安全使用 eval("2 + 2")不会产生任何副作用或问题。问题是您必须绝对确定要向 eval提供已知的良好代码。那么,对 eval("2 + " + x)的分析将是什么?在追溯所有可能的 x设置路径之前,我们无法确定。然后回溯用于设置 x的所有内容。然后追溯那些,等等,直到您发现初始值是安全的还是不安全的。如果它来自不受信任的地方,那么您有问题。
    示例:您只需要输入一部分URL并将其放在 x中。假设您有一个 example.com?myParam=42,所以您从查询字符串中获取 myParam的值。攻击者可以轻易地制作一个查询字符串,将其 myParam设置为可窃取用户凭据或专有信息并将其发送给自己的代码。因此,您需要确保您正在过滤 myParam的值。但是,您还必须经常进行相同的分析-如果您引入了新事物,现在又从cookie中获取 x的值,该怎么办?好吧,现在这很脆弱。
    即使 x的每个可能值都是安全的,您也不能跳过重新运行分析的过程。而且您必须定期执行此操作,然后在最佳情况下,只需说“确定就可以了”。但是,您可能还需要证明这一点。您可能只需要为 x填满一天。如果您又使用过 eval四次,则需要整整一周的时间。
    因此,只要遵守古老的格言“评估就是邪恶”。当然,不是必须的,但它应该是不得已的工具。

    关于javascript - JavaScript声明一个变量并在一个语句中使用逗号运算符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61077989/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com