JavaScript语言的三部分构成
按照相关的JS语法,去操作页面中的元素,有时还要操作浏览器里面的一些功能
1—ECMAScript(ES)3/5/6…:
JS的语法规范(变量,数据类型,操作语句等等),描述了该语言的语法和基本对象
2—DOM(document object model):
文档对象模型(描述处理网页内容的方法和接口),提供一些JS的属性和方法,用来操作页面中的Dom元素
3—BOM(browser object model):
浏览器对象模型(描述与浏览器进行交互的方法与接口),提供一些JS的属性和方法,用来操作浏览器的
JavaScript中的变量 variable
变量:可变的量。在编程语言中,变量就是一块内存空间,用来存储和代表不同值的东西。
- 是用来存储值的一块内存空间
- 若存储的是基本数据类型数据,则值数据本身就存储在该变量的内存空间中 【存的是值】
- 若要存储的值为引用数据类型,则该变量中存储的是值的【堆内存地址】 【存的是值的地址】
创建变量的几种方式
var / let / const / function / class / import / Symbol
//基于var创建变量n,让其指向具体的值10
var n = 10;
//创建变量m,但不未赋值,默认指向undefined
var m;
console.log(n,m);//=> 输出10 undefined
let a = 100;//基于let创建一个叫做a的变量,值100
a = 200; //修改值
console.log(a);//=>输出200
const b = 1000; //基于const创建变量b,值1000
b = 200;
console.log(b); //报错:Uncaught TypeError: Assignment to constant variable.指向不许被修改
//创建一个函数:也可以理解为创建一个变量func,让其指向这个函数
function func(){}
console.log(func);//=> 输出func函数本身 func(){}
//创建一个类:也可以理解为创建一个变量Parent,让其指向这个类
class Parent{}
console.log(Parent);//=>输出 class Parent{}
//基于模块规范来导入具体的某个模块:定义一个叫做axios的变量,用来指向导入的这个模块
import axios from './axios';//相当于let axios = require('./axios');
let c = Symbol (1000) //创建一个唯一值
各变量的特点和区别
- ES3/5中创建变量用 var 通过var定义的全局变量和函数都会成为window对象的属性和方法
- ES6中创建变量用 let、const(与var的区别在于变量提升)通过let、const创建的顶级声明不会定义在全局上下文中 但作用域链解析效果是一样
- let创建的是 变量 ,只不过他的指针指向可以随意的修改。
const创建的是 看上去像常量的变量 ,只不过他的 【指针指向一旦确定,就不能再修改】所以const创建的变量值是不允许被修改的**【看起来像但并不是常量】**
const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个引用类型(对象或数组)声明为常量可能会被修改const a = { name: "gao"}
let b = a;
b.name = "zhang"
console.log(b); //name: "zhang"
console.log(a); //name: "zhang" a被修改
// const a = [1, 2, 3]
// let b = a;
// b[0] = 3; 数组也一样
Function 函数【堆内存】function创建函数
- 函数的三种角色【会在函数章节再来细谈】
- 普通函数(作用域和作用域链)
- 构造函数(类/实例/原型和原型链)
- (在函数在作为构造函数执行时,会创建一个实例,此时的函数自身相当于一个类)
- 普通对象(键值对/属性值属性名)
- 函数的三种角色【会在函数章节再来细谈】
- Class 类【堆内存】 class创建类(ES6中创建自定义类的新语法)
- 导入模块 import导入模块【也可以创建变量】
- Symbol (1000) 创建一个唯一值
变量提升
当前作用域中JS 代码自上而下执行之前 浏览器会吧所有带有
var / function关键字的进行提前
声明或定义
var 关键字只是提前声明了一下 带function 关键字的在变量提升阶段吧声明和定义都完成了
//变量提升 => var x; var y; fn = xx001(堆地址)
console.log(x); //undefied
var x = 3, y = 4;
console.log(x);//3
console.log(fn);// ƒ fn() {}
function fn() {}
定义变量时 带var 和不带var区别?
console.log(a); //undefied
var a = 12;
console.log(a); //12
console.log(window.a); //12
console.log(a); //undefied
a = 12;
console.log(a); //12
console.log(window.a); //12
带var:在当前作用域中声明一个变量 如果是全局作用域 也就相当于给全局作用域设置一个属性叫a 也就是在window上添加了一个属性a
不加var : a 只是Window的一个属性 (把window省略了) 不是严格意义的变量
只对等号左边的进行变量提升
- = 赋值 左边是变量 右边永远是值
- = 右边也可以是三元表达式 a = b?c:d 但也是先运算出结果在赋值
- = 右边也可能是函数 这样的叫:匿名函数:函数表示式(把函数当做一个值)
console.log(fn) //undefined
var fn = function(){}
console.log(fn) //函数本身
sum(); //TypeError: sum is not a function
var sum = function(){}
// =>上面代码相当于直接输出sum 和console一样 但此时sum还没定义 所以报错
fn(); // aaa
function fn(){console.log("aaa");}
fn(); // aaa
//这种写法 fn已经提升并定义 所以 fu()在上或在下都可以运行
在真实项目中 应用这个原理 我们创建函数时可以用函数表达式 这样更严谨
因为只能对等号左边的进行提升,所以变量提升完成后 当前函数只是声明了,没有定义 想要执行函数只能放在赋值的代码之后执行,这样让我们代码更有逻辑
不管条件是否成立都要进行变量提升
var aa = 1;
function bb(){
//变量提升: 私有变量: var aa ,var b
aa = 33;
console.log(aa); //33 =>这里是函数作用域下第一次修改aa的值
if(1 === 1){
var b = 4;
var aa = 44
// aa = 44 如果不声明var 这里将修改全局都aa
}
console.log(aa,b);//44 4 =>第二次修改aa的值 b是4
}
bb()
console.log(aa); //1 全局 如果上面不声明var 这里值将为44
关于重名的处理
在变量提升阶段** 如果名字重复了 不会重新进行声明,但是会重新进行定义(后面的赋值会把前面的赋值给替换掉)
// 变量提升 : fn = (0x001)=(0x002)=(0x003)=(0x004)最后是4
fn() // => 4
function fn(){ console.log(1)} //(0x001)
fn() // => 4
function fn(){ console.log(2)} //(0x002)
fn() // => 4
var fn = 13 // => fn=13
fn() //这里将变成 : 13() 所以会报错 fn is not a function 停止运行
function fn(){ console.log(3)} //(0x003)
fn()
function fn(){ console.log(4)} //(0x004)
fn()
函数优先
每遇到一个 var 关键字的变量声明,首先会查询当前作用域之前是否已经有了该名称的变量,如果是,则会忽略该声明;如果没有则把该变量声明提升
变量声明和函数声明都会被提升,那么在重复声明的情况下
预解析时函数首先被提升,然后才到变量。
fn(); // 1
var fn;
function fn() { console.log(1); }
var fn = function() { console.log(2); }
fn(); // 2
虽然 var fn; 出现在 function fn (){…} 之前,但因为首先提升函数,而同名的 var 声明就被忽略了。尽管同名的 var 声明会被忽略掉,但是后出现的函数声明是能够覆盖前面的。
升只会提升函数声明,而不会提升函数表达式。
console.log(foo1); // [Function: foo1]
foo1(); // foo1
console.log(foo2); // undefined
foo2(); // TypeError: foo2 is not a function
function foo1 () {
console.log("foo1");
};
var foo2 = function () {
console.log("foo2");
}
// 这里可能会有人有疑问? 为foo2会报错,不同样也是声明?
// foo2在这里是一个函数表达式且不会被提升
一个函数提升案例
var a = 1;
function foo() {
a = 10;
console.log(a);
return;
function a() {};
}
foo();
console.log(a);
function a () {}等同于 var a = function() {};
var a = 1; // 定义一个全局变量 a
function foo() {
// 首先提升函数声明function a () {}到函数作用域顶端
// 然后function a () {}等同于 var a = function() {};最终形式如下
var a = function () {}; // 定义局部变量 a 并赋值。
a = 10; // 修改局部变量 a 的值,并不会影响全局变量 a
console.log(a); // 打印局部变量 a 的值:10
return;
}
foo();
console.log(a); // 打印全局变量 a 的值:1
暂时性死区
ES6 明确规定,代码块({})中如果出现 let 和 const 声明的变量,这些变量的作用域会被限制在代码块内,也就是块级作用域
console.log(a); //undefined
var a = 1;
console.log(b); //报错 Cannot access 'b' before initialization
let b = 1;
var a = 1;
if(true){
a = 2;
console.log(a); //??
let a; 如果是var 结果又是多少
}
console.log(a); //??
var a = 1;
if(true){ //这里加if 是为了一个块级作用域 也为了可以读到全局都a
// 死区开始--------------------------
// 访问 a 都会报错,不能在声明之前使用
a = 2;
console.log(a);
// 死区结束-------------------------- 这里之前都会报错代码也不会往下执行 let\const必须先声明在使用
// 以下是常规写法
let a; //如果这里是 var 则会变量提升到全局 修改全局都a
console.log(a); // undefined
a = 3;
console.log(a); // 3
}
console.log(a); //1 如果是var a的值被修改成3
let 和 const 声明变量之前不可以使用这些变量,这就是所谓的 暂时性死区。
总之:let \ const必须先声明在使用
初步了解执行上下文、作用域链
全局上下文是最外层的上下文,也就是我们常说的window ,通过var定义的全局变量和函数都会成为window对象的属性和方法 ,通过let、const创建的顶级声明不会定义在全局上下文中 但作用域链解析效果是一样。
上下文在其代码都执行完毕后会被销毁,包括定义在它上面的所有变量和函数(全局上下文在应用程序退出才会销毁,例如关闭网页退出浏览器)
作用域链
函数执行形成一个私有作用域(保护私有变量)进入到私有作用域中 首先变量提升(声明过的变量都是私有的) 接下来执行代码
- 执行时遇到变量 如果这个变量是私有 那么按照私有处理即可
- 如果当这个变量不是私有 就向上级查询 一直查到window全局作用域为止 这种机制叫
作用域链
- 如果上级作用域没有这个变量(找到window也没有):
变量= 值 相当于给window设置了这个属性 以后再操作window下就有了
如果直接输出(alert(变量))此时没有就会报错
console.log(x, y); // undefined x 2
var x = 10,
y = 20; // x = 10;y=20
function fn() {
//开辟堆内存 形成一个私有作用域
// 变量提升 var x (私有变量)
console.log(x, y); // x为undefined y是20【全局】
var x = y = 100; // x =100 (私有) y = 100 【全局】
console.log(x, y); // 100 100
}
fn();
console.log(x, y); // 10 100