1.全局对象GO
全局环境:浏览器中,所有的script标签都在一个环境中,这个环境就叫做全局环境
全局对象(Global Object):浏览器环境中,js引擎会整合所有的script标签内容,产生一个全局对象,这个对象就叫做window对象
全局变量:在全局环境下定义的变量,script标签下定义的变量
全局函数:在script标签下定义的函数
特点
通过打印window可以看出,全局变量是作为window的一个属性存在的
2.活动对象AO
活动对象(Active Object):在函数被调用的时候产生的一个对象,用来保存当前函数内部的执行环境
局部变量:在函数内部定义的变量
局部函数:在函数内部定义的函数
local(本地)就是局部环境,走过a之后,对a进行了赋值
函数调用完之后就立即销毁,只能通过断点调试的方式来查看局部环境。
需要进入函数内部查看('本地'就代表局部环境
f2其实是局部对象AO的一个方法
关系
局部变量其实是作为活动对象(AO对象)的一个属性存在的
局部函数其实是作为活动对象(AO对象)的一个方法存在的
3.全局预编译
概念
在全局环境(script标签)下JS引擎的处理方式
流程
- 查找全局变量的声明,作为GO对象的属性名,值是undefined
- 再查找全局函数的声明,作为GO对象的属性名,值是function(代表这个函数)
- 从上往下依次执行代码(声明的语句会被略过)
预编译:
console.log(a) // 函数a
var a = 10
console.log(a) // 10
function a(){
console.log(a)
}
a() // 报错
产生的原因:代码有歧义,定义的变量名a和函数名a冲突了。导致程序报错。
想要了解报错的本质,需要知道js预编译的过程
js预编译的目的:消除歧义,让代码能够从上到下依次执行。(也称之为预处理)
具体流程
- 先生成GO对象(window对象)
- 再进行全局预编译
-
- 先查找全局变量的声明,作为GO对象的属性名
GO : {a : undefinde}
-
- 再查找全局函数的声明,作为GO对象的属性名,值是function(代表这个函数)
GO : {a : function}
-
- 从上往下依次执行代码(声明的语句会被略过)
执行第1行:打印a,打印function
执行第2行:给a赋值 GO : {a : 10}
执行第3行:打印a,打印的值是10
执行第7行:调用a,a现在是一个变量10,不能调用方法,报错
结论:在全局环境中,如果函数和变量同名,函数的优先级高,会被优先调用。
4.函数预编译
概念
在局部环境(函数里面)下JS引擎的处理方式
流程
- 在函数被调用的那一刻,就会为当前的函数生成一个AO对象。
- 查找局部变量的声明,作为AO对象的属性名,值是undefined
- 使用实参的值,替换形参的值
- 再查找局部函数的声明,作为AO对象的属性名,值是function(代表这个函数)
- 从上往下依次执行代码(声明的语句会被略过)
<script>
function a(n){
console.log(m)
var m = 10 // 局部变量
console.log(b)
function b(){ // 局部函数
console.log("bbb")
}
b()
}
a(100)
</script>
具体流程
- 生成GO对象
- 全局预编译
-
- 查询全局变量 没有
- 查询全局函数 有 GO{a ; function}
- 执行script里面的代码:声明的语句会被略过,直接执行11/24行,调用a函数。此时就会进行函数预编译
-
- 在函数被调用的那一刻,就会为当前的函数生成一个AO对象。
- 查找局部变量的声明,作为AO对象的属性名,值是undefined
AO{ m : undefined, n : undefined}
-
- 使用实参的值,替换形参的值
AO{ m : undefined, n : 100}
-
- 再查找局部函数的声明,作为AO对象的属性名,值是function(第2/10行)
AO{ m : undefined, n : 100 , b : function}
-
- 从上往下依次执行代码(声明的语句会被略过)
执行第3/11行,打印m,打印的是undefined
执行第4/13行,给m赋值 AO{ m : 10, n : 100 , b : function}
执行第5/15行,打印b 打印的是function
执行第9/21行,调用b 打印bbb
结论:在局部环境中,如果函数和变量同名,函数的优先级高,会被优先调用。
5.作用域
概念
就是一个区域,用来限定变量在某个范围内起作用
优点
提高程序可靠性,减少命令冲突(ES6语法规定,在同一个域中,不能定义相同的变量,否则会报错)
分类
全局作用域
在全局环境下
由script标签产生的区域,从计算机角度可以理解为window对象(GO对象)管控的区域
全局变量和全局函数在全局作用域下,在页面关闭的时候被销毁
局部作用域
在函数里
由函数产生的区域,从计算机角度,可以理解为当前函数对象(AO对象)管控的区域
局部变量和局部函数在局部作用域下,在函数执行完之后被销毁
块级作用域
在{}中(ES6)
if(true){
var num1 = 20 // ES5语法,没有块级作用域概念,num1不受{}}影响
let num2 = 30 // ES6语法,有块级作用域影响,num2出了{}}就无法使用
}
函数形参的本质
本质是函数内部的一个局部变量。
let定义的变量在同一个域中不能重复
ES6语法,存在于if语句,for循环等{}中,{}包含的区域就是块级作用域,
6.作用域链
概念
只要是代码,都会在一个作用域中,写在函数内部的就在局部作用域;没写在函数内部的,就写在全局作用域。
如果这个作用域内部还有函数,那么在这个作用域内,又可以诞生一个新的作用域,就形成作用域链
function f1(){
var num1 = 10
function f2(){
console.log(num1)
}
f2()
}
f1()
console.dir(f1)
f1的作用域链
[[Scopes]]: Scopes[1]
0: Global {window: Window, self: Window, document: document, name: '', location: Location, …}
f2的作用域链
[[Scopes]]: Scopes[2]
0: Closure (f1) {num1: 10}
1: Global {window: Window, self: Window, document: document, name: '', location: Location, …}
作用
提供了一个查找机制,内部函数可以访问外部函数中的变量,使用链式查找来决定哪些数据能被内部函数访问。
本质
就是一个数据结构,函数内部是可以嵌套函数的,每一次嵌套都会形成新的作用域,把这些作用域串起来,就形成了作用域链。
具体流程
- JS引擎加载html页面,产生GO对象,在全局作用域中
GO{a :function}
- 全局预编译完成后,调用a函数,在调用那一刻,会为a函数产生一个a函数的AO对象,在局部作用域中。
现在a函数内部有两个作用域,一个是GO所在的全局作用域,一个是a函数所在的局部作用域AO
[scopes] ---- aAO { b : function }
---- GO { a : function }
- a函数预编译完成后,会调用b函数,在调用那一刻,会为b函数产生一个b函数的AO对象,在局部作用域中
现在b函数内部有三个作用域,GO(1),a函数(0),b函数(本身)
[scopes] ---- bAO { c : function }
---- aAO { b : function }
---- GO { a : function }
- b函数预编译完成之后,会调用c函数,在调用那一刻,会为c函数产生一个c函数的AO对象,在局部作用域中
现在b函数内部有四个作用域,GO(2),a函数(1),b函数(0),c函数(本身)
[scopes] ---- cAO { }
---- bAO { c : function }
---- aAO { b : function }
---- GO { a : function }
function a(){
var num = 10
function b(){
console.log(num)
}
b()
}
a()
通过作用域链的角度分析,为什么打印的是10
分析程序的执行流程
- 产生GO对象,进行全局预编译
[scopes] -- GO { a:function }
- 全局预编译完后,调用a函数,就会产生a函数的AO对象,此时a函数预编译之后的结果
[scopes] -- aAO { num:undefined,b:function }
-- GO { a:function }
预编译a函数之后,执行a函数
执行num赋值时,num就有值了,aAO { num:10,b:function }
执行b函数调用
- 调用b函数时,就会产生b函数的AO对象,此时b函数预编译的结果
[scopes] -- bAO { }
-- aAO { num: 10,b:function }
-- GO { a:function }
预编译完b函数后,执行b函数
执行打印num的时候,此时bAO当中没有定义num,它就会根据作用域链到上一层aAO去找
7.原型和this指向
实例成员和静态成员
构造函数中定义的属性和方法都叫成员
实例成员
在构造函数中,通过this添加的成员(name,age)都是实例成员。
实例化成员只能通过实例化对象来调用(new出来的叫实例化对象)
var zxy = new Start('张学友',60)
静态成员
在构造函数本身上添加的成员,叫静态成员
静态成员只能通过构造函数名来访问
console.log(Start.country)
Start.country = '中国'
console.log(ldh.country) // undefined
console.log(Start.country) // 这样才能调用静态成员
静态成员作用
把一些公共的东西(毕业院校,国籍,民族等)放在静态成员中,可以减少内存分配
举例:饮水机和水杯
如果饮水机定义成实例,就相当于每一个学生都有一个饮水机,浪费资源
如果把水杯定义成静态,就相当于大家共用一个水杯,不太好。一般定义成实例的
原型的由来
解决的问题:使用this定义方法时,每new一次,都会为对象的方法开辟一块空间,浪费内存
new做的事
- 在堆区中开辟一块空间
- 给空间中的属性和方法赋值
- 返回空间中的首地址给栈区中的变量名
存在的问题:每创建一次对象,就会为对象里面的方法分配一个内存空间,比较浪费内存。
function Start(name,age){
this.name = name
this.age = age
// this.sing = function(){
// console.log('会唱歌')
}
console.dir(Start)
// 类中有一个prototype属性,可以看做是类的静态成员,可以把公共的类定义在上面
// prototype属性一般称为原型对象
console.log(typeof Start.prototype) // object对象类型
// 使用原型对象解决内存重复占用的问题
Start.prototype.sing = function(){
console.log('会唱歌')}
8.本地储存
概念
Web 存储 API 提供了 sessionStorage (会话存储) 和 localStorage(本地存储)两个存储对象来对网页的数据进行添加、删除、修改、查询操作。
- localStorage 用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去除。(本地存储)
- sessionStorage 用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据。(会话存储)
特点
数据存在用户的浏览器中
可以方便用户设置和读取数据,刷新页面不会丢失数据
容量有限,sessionStorage 约5m,localStorage约20m
只能存储字符串,可以将对象使用JSON.stringify转成jdon后存储
存储对象方法
| 方法 | 描述 |
|---|---|
| key(n) | 返回存储对象中第 n 个键的名称 |
| getItem(keyname) | 返回指定键的值 |
| setItem(keyname, value) | 添加键和值,如果对应的值存在,则更新该键对应的值。 |
| removeItem(keyname) | 移除键 |
| clear() | 清除存储对象中所有的键 |
object.key(localStorage) 获取浏览器本地存储的所有键值对
// 获取文本框中的数据
var input = document.querySelector('input')
// 存储数据
document.querySelector('.c1').addEventListener('click',function(){
sessionStorage.setItem('username',input.value)
sessionStorage.setItem('password',input.value)
})
// 清空数据
document.querySelector('.c4').addEventListener('click',function(){
sessionStorage.clear()
})
// 新建窗口.空白窗口,但可以看到之前存储的数据
document.querySelector('.c5').addEventListener('click',function(){
window.open('http://网址.html')
// 保持ip和端口一致才能共享数据
})
会话存储特点
sessionStorage
生命周期为关闭浏览器
在同一个窗口(页面)可以数据共享
以键值对形式存储数据
localStorage
生命周期永久有效,直到手动删除或电脑报废
在多个窗口(页面)可以数据共享(仅限于同一个浏览器
以键值对形式存储数据