一、let
let 是用来声明变量,类似 var,但是其声明的变量,只在声明的代码块内有效。并且不允许在相同作用域内,重复声明同一个变量;也不能在函数内部重新声明参数。
for(let i =0;i<3;i++){
let tempObj = {name:'tom'}
//let tempObj = {} //SyntaxError: Identifier 'tempObj' has already been declared
var tempArr = [23]
}
console.log(tempObj) //tempObj is not defined
console.log(tempArr) //[23]
let 命令所声明的变量一定要在声明后使用,否则报错。
var 命令会发生“变量提升”现象,即变量可以在声明之前使用,值为 undefined。
console.log(a); // 输出 undefined
var a= 2;
console.log(b); // 报错 ReferenceError
let b= 2;
在块级作用域内部的声明函数,建议不要使用函数声明语句;优先使用函数表达式。
因为函数声明类似于 var 命令,会发生变量提升。即把函数声明提升到其所在的块级作用域的头部。
// 建议不要使用
{
let a = 'secret';
function f() {return a;}
// 相当于
//var f= function () { // f 会变量提升
// return a;
//}
}
// 优先使用函数表达式
{
let a = 'secret';
let f = function () {return a;};
}
块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。函数声明也是如此。
if (true) let x = 1; // 会报错
if (true) { // 正常
let x = 1;
}
二、const
声明一个只读的常量。一旦声明,常量的值就不能改变。必须立即初始化,不能留到以后赋值。
其作用域与 let 命令相同:只在声明所在的块级作用域内有效。
const 只能保证这个指针是固定的,也就是说对象中的属性是可变的。
可以使用 Object.freeze 方法。可以将声明的对象冻结
var constantize = (obj) => {Object.freeze(obj);
Object.keys(obj).forEach((key, i) => {if ( typeof obj[key] === 'object' ) {constantize( obj[key] );
}
});
};
声明变量的六种方法
var
function
let
const
import
class
var 命令和 function 命令声明的全局变量,是顶层对象的属性;而 let、const、class 命令声明的全局变量,并不属于顶层对象的属性。
var a = 1;
window.a // 1
let b = 1;
window.b // undefined
js 中获取顶层对象的两种方法。
function foos() {
(typeof window !== 'undefined'
? window
: (typeof process === 'object' &&
typeof require === 'function' &&
typeof global === 'object')
? global
: this);
}
// 方法二
var getGlobal = function () {if (typeof self !== 'undefined') {return self;}
if (typeof window !== 'undefined') {return window;}
if (typeof global !== 'undefined') {return global;}
throw new Error('unable to locate global object');
};
在 ES2020 语言标准中,引入 globalThis 作为顶层对象。任何环境下,globalThis 都是存在的,都可以从它拿到顶层对象,指向全局环境下的 this。
// 两次打印的对象是相同的
console.log(globalThis)
class Infos {gets() {console.log(globalThis)
}
}
let ss = new Infos();
ss.gets();
三、js 原型链
1、对象有__proto__属性,函数有 prototype 属性;对象由函数生成。
2、生成对象时,对象的__proto__属性指向函数的 prototype 属性。
3、函数也是对象的一种,所以函数有__proto__属性
// 首先我们先创建一个构造函数 Foo
let Foo = function() {}
// 实例化
let f1= new Foo();
console.log(f1.__proto__ === Foo.prototype) //true
// 而 Foo 函数对象都是由 Function 函数生成的:console.log(Foo.__proto__ === Function.prototype) //true
console.log(Foo.prototype.constructor === Foo) //true
//Function 函数本身作为对象时,生成它的函数是他自身!console.log(Function.__proto__ === Function.prototype) //true
console.log(Function.prototype.constructor === Function) //true
// 函数默认的 prototype 是系统自动生成的一个对象:console.log(Foo.prototype.__proto__ === Object.prototype) //true
console.log(Function.prototype.__proto__ === Object.prototype) //true
// 新建对象
let o1= new Object(); // 或者 let objs = {}
console.log(o1.__proto__ === Object.prototype) //true
//Object 函数既然是函数,那生成它的函数自然是 Function 函数
console.log(Object.__proto__ === Function.prototype) //true
console.log(Object.prototype.constructor === Object) //true
通过以上的分析可以得出原型链图,如下图所示: