导航
function test(a) {
let a = 10;
console.log(a);
}
test();
// 答案:
// SyntaxError: Identifier 'a' has already been declared
console.log(a);
let a = 1;
// 答案:
// Uncaught ReferenceError: Cannot access 'a' before initialization
function test() {
console.log(a);
let a = 10;
}
test();
// 答案:
// Uncaught ReferenceError: Cannot access 'a' before initialization at test
var a = a;
console.log(a);
// 答案:
// undefined
let b = b;
console.log(b);
// 答案:
// Uncaught ReferenceError: Cannot access 'b' before initialization
var a = 1;
{
let a = a;
console.log(a);
}
// 答案:
// Uncaught ReferenceError: Cannot access 'a' before initialization
console.log(typeof a);
let a = 10;
// 答案:
// Uncaught ReferenceError: Cannot access 'a' before initialization
function test(x = y, y = 2) {
console.log(x, y); // 报错
// Uncaught ReferenceError: Cannot access 'y' before initialization
}
test();
// 下面可行
function test(x = 1, y = x) {
console.log(x, y);
}
test();
if (true) {
let a = 1;
}
console.log(a); // Uncaught ReferenceError: a is not defined
for (;1;) {
let a = 1;
}
console.log(a); // 不会执行,所以也不会报错
// 因为 上边 for 循环会 死循环
for (var i = 0; i < 10; i++) {
var i = 'a';
console.log(i); // a
}
for (let i = 0; i < 10; i++) {
}
console.log(i); // Uncaught ReferenceError: i is not defined
for (var i = 0; i < 10; i++) {
i = 'a';
console.log(i);
}
// 打印1个a
// 解析:
var i = 0;
for (; i < 10;) {
i = 'a';
console.log(i);
i++;
}
// 1. 第一次循环完毕,i = 'a' 打印 'a',i++ => NaN
// 2. 第二次循环 NaN < 10 不成立,不执行for循环里边代码
for (let i = 0; i < 10; i++) {
i = 'a';
console.log(i);
}
// 打印1个a
for (let i = 0; i < 10; i++) {
let i = 'a';
console.log(i);
}
// 打印10个a
if (1) {
let a = 0;
{
let a = 'a';
}
}
// for循环的let声明相当于for循环内部的父级
// 所以内外不影响
for (let i = 0; i < 10; i++) {
var i = 'a'; // i被重复定义
console.log(i);
}
// SyntaxError: Identifier 'i' has already been declared
// for内部的var i为什么报错呢?可以类比成
if (1) {
let a = 0;
{
var a = 'a';
}
}
// 而这个var是会有个类似dom冒泡的机制,有变量提升,会变成
if (1) {
var a = undefined;
let a = 0;
{
a = 'a';
}
}
由于同一作用域下let不允许重复定义变量,所以报错
if (1) {
return '233';
} // 返回不出去
// 如何才能可以有返回值:用do,但是兼容性不好
do {
return 1;
}
var
:函数作用域。使用 var
声明的变量在函数内部是局部变量,但在块级作用域(例如 if
、for
块)内声明的变量仍然是全局或函数级别的。let
和 const
:块级作用域。使用 let
和 const
声明的变量仅在其声明的块内可见。var
:变量声明会被提升到函数或全局作用域的顶部,但初始化不会提升。这意味着你可以在声明之前访问变量,但值为 undefined
。let
和 const
:变量声明也会被提升,但不会初始化。这意味着在变量声明之前访问会导致引用错误(ReferenceError)。var
:允许在同一作用域内重复声明同一个变量。let
和 const
:在同一作用域内不允许重复声明同一个变量。var arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = function() {
console.log(i);
}
}
for (var i = 0; i < 10; i++) {
arr[i](); // 打印 0-9,原本如果打印应该是10个10、但由于此处
// 第二个for循环中的i把上一个for循环最后的i=10覆盖了
// 导致每次循环i都是当前循环的i值
}
var arr = [];
for (let i = 0; i < 10; i++) {
arr[i] = function() {
console.log(i);
}
}
for (var j = 0; j < 10; j++) {
arr[j]();
}
// 打印0-9、每次循环let都是一个新值,而不是全局的i。类似于闭包
// 会把i缓存下来
let a = 0;
function a() {}
console.log(a);
// Identifier 'a' has already been declared
let a = 0;
{
function a() {}
}
console.log(a);
// 打印0
let a = 0;
{
var a = function a() {}
}
console.log(a);
// Uncaught SyntaxError: Identifier 'a' has already been declared
// let a = 0;
{
function a() {}
}
console.log(a);
// [Function: a]
let a = 0;
{
a();
function a() {
console.log(10);
}
}
console.log(a);
// 10
// 0
// 和上边代码的区别:
// 在第一段代码中,外部作用域中的 a 被声明为 let,因此它是块级作用域变量,不会被块内的函数声明覆盖或改变。
// 在第二段代码中,外部作用域中没有任何变量 a,因此在块作用域结束后,a 被提升并初始化为块内声明的函数。
let a;
{a} = {a: 1};
console.log(a);
// SyntaxError: Unexpected token '='
let a;
({a} = {a: 1}); // 加上括号使其成为表达式
console.log(a); // 1
let a = [1,2,3],
obj = {};
[obj.a, obj.b, obj.c] = a;
console.log(obj.a, obj.b, obj.c); // 1 2 3
let ({a: b}) = {};
// ReferenceError: let is not defined
const a;
console.log(a);
// Uncaught SyntaxError: Missing initializer in const declaration
const a = 0;
a = 1;
// TypeError: Assignment to constant variable.
const a = 0;
var a = 1;
// SyntaxError: Identifier 'a' has already been declared
{
const a = 0;
}
console.log(a);
// ReferenceError: a is not defined
{
console.log(a);
const a = 0;
}
// ReferenceError: Cannot access 'a' before initialization
const person = {};
person.name = 'Lance';
console.log(person);
// { name: 'Lance' }
const person = {};
Object.freeze(person);
person.name = 'Lance';
console.log(person);
// {}
只能冻结一层:
const person = {
name: {
c: "a",
},
};
Object.freeze(person);
person.name.c = "1111";
console.log(person); // { name: { c: '1111' } }
function myFreeze(obj) {
Object.freeze(obj);
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof(obj[key]) === 'object' && obj[key] !== null) {
myFreeze(obj[key]);
}
}
}
}
const person = {
name: 'Lance',
grade: {
Math: 100,
IT: 90,
Chinese: 100
},
a: {
b: {
c: 1
}
}
}
myFreeze(person);
person.a.b.c = 2333;
console.log(person);
var a = 1;
// 等效于
window.a = 1;
const a = 1;
console.log(window.a); // undefined
var x = x;
let x = x;
// 两者区别
var x = x; // x有变量提升
// 1. var x = undefined;
// 2. x = undefined;
let x = x;
// 1. let x = x; 中,右侧的 x 试图在声明 x 之前使用它自己,但此时 x 还没有被初始化(因为变量的声明和赋值在同一行)。
// 2. 在 let 声明之前,变量 x 是未定义且不可用的,所以会报错。
var x = 1;
function foo(x, y = function() { x = 2; console.log(x) }) {
var x = 3;
y();
console.log(x);
}
foo();
console.log(x);
接着函数执行,x赋值为3,执行函数y,函数内没有,函数参数中有y:
执行y:
把参数内部的x赋值为2:
接着点击执行栈中的foo我们进行查看,会发现函数参数中y执行改掉的x是函数参数内部的x,而不是foo函数体中的变量x:
所以最终结果:
var x = 1;
function foo(x, y = function() { var x = 2; console.log(x) }) {
var x = 3;
y();
console.log(x);
}
foo();
console.log(x);
// 答案:
// 2 3 1 和上面一样
可以把上面代码简化看成如下
{
let x = 2;
{
let x = 3;
console.log(x)
}
console.log(x);
}
var x = 1;
function foo(x, y = function() { x = 2; console.log(x) }) {
x = 3;
y();
console.log(x);
}
foo();
console.log(x);
// 答案:
// 打印 2 2 1
// 分析
// 1. 函数参数内部声明了x,y
// 2. 函数执行 x = 3
// 3. 由于函数内部没有声明x,所以去函数参数中找x,找到了,赋值x=3
// 4. 接着执行y(),y函数内部又把x从3改回了2
// 5. y函数内部打印x为2
// 6. foo函数内部打印x也是2
// 7. 全局GO中的x没有变化,仍为1
可以把上面代码简化看成如下
{
let x = 2;
{
x = 3;
console.log(x)
}
console.log(x);
}
var x = 1;
function foo(x, y = function() { var x = 2; console.log(x) }) {
let x = 3;
y();
console.log(x);
}
foo();
// 报错:SyntaxError: Identifier 'x' has already been declared