1.可变性JavaScript中有七种基本数据类型(string、number、boolean、undefined、symbol、bigint和null),它们是不可变的。这意味着一旦分配了一个值,我们就不能修改它们,我们能做的就是将它重新分配给一个不同的值(不同的内存指针)。另一方面,其他数据类型如Object和Function是可变的,这意味着我们可以修改同一内存指针中的值。//Q1lettext='abcde'text[1]='z'console.log(text)//abcde字符串是不可变的,所以一旦赋值就不能更改为不同的值,你可以做什么是重新分配它。请记住,更改值与重新分配给另一个值不同。//Q2constarr=[1,2,3]arr.length=0console.log(arr)//[]将arr.length赋值为0等同于清空数组,所以此时数组会变空观点。//Q3constarr=[1,2,3,4]arr[100]=undefinedconsole.log(arr,arr.length)//[1,2,3,4,empty×96,undefined]101因为数组占用是连续的内存位置,所以当我们将索引100分配给一个值(包括未定义)时,JavaScript会保留从索引0到索引100的内存,这意味着数组现在的长度为101。2.var和boost//Q4varvariable=10;(()=>{variable2=100console.log(variable)console.log(variable2)variable=20varvariable2=50console.log(variable)})();console.log(variable)varvariable=30console.log(variable2)//10//100//20//20//ReferenceError:variable2isnotdefinedvar是函数作用域的变量,而let和const是块级的域变量,只能提升var,这意味着变量声明总是移到顶部。由于提升,您甚至可以在使用var关键字声明变量之前分配、调用或使用变量。let和const无法提升,因为它启用了TDZ(临时死区),这意味着变量在声明之前不可访问。在上面的示例中,variable2在函数内部声明,var关键字使变量仅在函数范围内可用。因此,当函数外部的任何内容尝试使用或调用该变量时,将抛出referenceError。//Q5test()//没有错误functiontest(){cconsole.log('test')}test2()//errorvartest2=()=>console.log('test2')functionkeyworddeclarationFunctionscan提升函数语句,但箭头函数不能,即使它们是使用var声明的。3.意外的全局变量//Q6functionfoo(){leta=b=0;一个++;返回a;}foo();b类型;//数字类型;//undefinedconsole.log(a)//错误:ReferenceError:a未定义var是函数作用域,let是块作用域变量。虽然看起来a和b都是使用let(leta=b=0)声明的,但实际上变量b被声明为全局变量并分配给Window对象。换句话说,它类似于:functionfoo(){window.b=0;让a=b;一个++;}4.闭包//Q7constlength=4;常量fns=[];常量fns2=[];for(vari=0;iconsole.log(i));}for(leti=0;iconsole.log(i));}fns.forEach(fn=>fn());//4444fns2.forEach(fn=>fn());//0123闭包是对变量环境的一种保护,即使变量已经改变或被垃圾回收。在上面的问题中,区别在于变量声明,其中第一个循环使用var,第二个循环使用let。var是函数作用域变量,因此当在for循环块内声明时,var被视为全局变量而不是内部变量。另一方面,let是一个块作用域的变量,类似于Java和C++等其他语言中的变量声明。在这种情况下,闭包只发生在let变量中,每个推入fns2数组的函数都会记住变量的当前值,而不管变量将来是否发生变化。相反,fns不记住变量的当前值,它使用全局变量的未来或最终值。5.对象//Q8varobj1={n:1}varobj2=obj1obj2.n=2console.log(obj1)//{n:2}//Q9functionfoo(obj){obj.n=3obj.name='test'}foo(obj2)console,log(obj1)//{n:3,name:'test'}我们知道,对象变量只包含指向对象内存位置的指针,所以这里obj2和obj1指向同一个对象。这意味着如果我们更改obj2的任何值,obj1也会受到影响,因为它们本质上是同一个对象。同样,当我们在函数中将对象作为参数传递时,传递的参数只包含对象指针。因此,函数可以直接修改对象而不返回任何内容,这种技术称为引用传递。//Q10varfoo={n:1};变量栏=富;安慰。日志(富===栏);//真富。x=foo={n:2};安慰。log(foo)//{n:2}console.log(bar)//{n:1,x:{n:2}}console.log(foo===bar)//false因为对象变量只包含指向对象内存位置的指针,所以当我们声明varbar=foo时,foo和bar都指向同一个对象。在接下来的逻辑中,foo={n:2}首先运行,其中foo被分配给一个不同的对象,因此foo有一个指向不同对象的指针。与此同时,foo.x=foo正在运行,这里foo仍然包含旧指针,所以逻辑类似于:foo={n:2}bar.x=foosobar.x={n:2},最后foo的值是{n:2}而bar是{n:1,x:{n:2}}。6.this//Q11constobj={name:"test",prop:{name:"propname",print:function(){console.log(this.name)},},print:function(){控制台.log(this.name)}print2:()=>console.log(this.name,this)}obj.print()//testobj.prop.print()//propnameobj.print2()//undefined,windowglobalobject上面的例子展示了this关键字在一个对象中是如何工作的,这里this指的是执行函数中的执行上下文对象。但是,此作用域仅适用于普通函数声明,不适用于箭头函数。上面的例子展示了显式绑定,比如在object1.object2.object3.object4.print()中,打印函数会使用最新的对象object4作为thiscontext,如果this没有绑定到一个对象,它会回退到rootobject,它是调用obj.print2()时的Window全局对象。另一方面,你也必须理解对象上下文之前已经绑定的隐式绑定,所以下一个函数执行总是使用那个对象作为这个上下文。例如:当我们使用func.bind(