手写 —— 实现一个 new 操作符
function Student(name) { |
new 做了哪些事
- 创建一个新的空对象,类型是 Student
- 将 this 指向这个新的对象
- 执行构造函数 目的:给这个新对象加属性和方法
- 返回这个新对象
简单实现
function NEW(fun) { |
const stu = NEW(Student, 'ls') |
function Student(name) { |
new 做了哪些事
简单实现
function NEW(fun) { |
const stu = NEW(Student, 'ls') |
lodash 插件 cloneDeep
数组:slice()/concat()/Array.from()/扩展运算符
对象:Object.assign()/扩展运算符
这种方法只能复制 JSON 格式支持的属性名和值,不支持的属性名和值会直接忽略:会忽略 undefined、symbol,不能序列化函数,不能解决循环引用的对象 参考MDN
JSON.parse(JSON.stringify({ |
function deepCopy(source) { |
Object.fromEntries() 方法把键值对列表转换为一个对象,是 Object.entries 的反转
循环引用(环)
解决思路: 通过一个WeakMap来存储拷贝过的对象
let hash = new WeakMap() |
特殊对象的拷贝
// 拷贝 Function |
防抖和节流都是为了解决短时间内大量触发某函数而导致的性能问题,比如触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象
在事件被触发 n 秒后再执行回调函数,如果在这 n 秒内又被触发,则重新计时(短时间内连续触发的事件 只有效执行一次)
应用场景
用户在输入框中连续输入一串字符后,只会在输入完后去执行最后一次的查询请求,这样可以有效减少请求次数,节约请求资源
window 的 resize、scroll 事件,不断地调整浏览器的窗口大小、或者滚动时会触发对应事件,防抖让其只触发一次
规定一个单位时间 n,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内事件被触发多次,只有一次能生效(每 n 秒触发一次)
应用场景
鼠标连续不断地触发某事件(如点击),n 秒内只触发一次
监听滚动事件,比如是否滑到底部自动加载更多
防抖的作用是将多个连续的debounced调用合并为一次callback调用。防抖是基于最近次 debounced 调用来重置 waitTime,如果debounced事件触发间隔小于 waitTime,callback就不会执行;
节流的作用是限制callback调用的频率(每waitTime调用一次)。是基于上次 callback 调用来计算 waitTime 的,不管callback 事件触发有多频繁,只要距离上次 callback 调用超过了 waitTime,就一定会进行下次 callback 调用。
– 原理:
防抖是 debounced 维护了一个计时器,规定在 waitTime 时间后触发 callback,但是在 waitTime 时间内再次触发 debounced 的话,会清除当前的 timer 然后重新计时,这样一来,只有最后一次debounced 操作才能触发 callback;
节流是通过判断是否到达一定时间 (waitTime) 来再次触发 callback , func 在 waitTime 时间内不能被再次触发。
实现
throttle-debounce 插件
简单实现
// 节流 |
注意事项
getElementById() 只能由 document 调用,如果通过其他元素调用会报错,其他方法可由已经获取到的 dom 对象调用
querySelector和 getElementById如果获取不到元素会返回 null;getElementsByClassName 、getElementsByTagName 、getElementsByName 、querySelectorAll 如果没有获取到元素也会返回一个伪数组,只不过伪数组长度为 0
focus : 获得焦点blur : 失去焦点click : 单击dblclick :双击mouseover/mouseout : 进入/离开元素mouseenter/mouseleave : 进入/离开元素mousedown :按下mouseup :抬起/释放mousemove :移动keydown/keypress :按下keyup: 释放我们之前讨论的属性,都是 HTML 规范中,标签本来就有的属性,对于标签自定义的一些属性,比较特殊
在 html 页面中,定义一个自定义属性
<div id="box" aa="bb"></div> |
在对应的 DOM 对象中是不存在的,在 DOM 对象中只会存在固定的那些属性
var box = document.getElementById('box') |
attribute 方法
attribute 系列方法用于设置标签的属性,不管是自定义的还是固有的属性
// 获取标签的属性 |
区别 :
// <div a="1" id="box"></div> |
[案例:获取当前元素的索引]
// 方式1: |
【案例:tab 栏切换】
var div = document.getElementById('div') |
浏览器兼容性:指网页在各种浏览器上的显示效果不一致。或者是一些属性和方法在低版本的浏览器中不支持
书写 innerText 的兼容性代码
function getInnerText(element) { |
标签不仅可以通过 class 属性操作样式 (嵌套样式),还可以通过 style 属性操作样式 (行内样式)。
同样的 DOM 对象可以通过 className 操作样式 (嵌套样式),也可以通过 style 属性操作样 (行内样式)。
css : 嵌套样式 => js : 类名 div.className = ‘red’
css : 行内样式 => js : style 对象 div.style.color = ‘red’
- ,比如 background-color ,在 style 对象中使用 backgroundColor => (因为 - 在 js 中不是一个合法的标识符)<div style="color:red;background-color:blue;">哈哈</div> |
var div = document.querySelector('div') |
getComputedStyle 获取元素计算后的样式
语法:
window.getComputedStyle( 获取的元素, 伪类) 伪类 ==> ::after ::before,如果写上了伪类,表示要获取元素的伪类的样式,如果不需要获取的话,该参数写 null
返回值: 返回一个样式对象
var div = document.querySelector('div')
// 获取元素自身的
var ret = window.getComputedStyle(div, null).fontSize
var ret1 = window.getComputedStyle(div, null).backgroundColor
// 推荐:对于复合样式,需要获取什么样式,写具体的样式名,这样能更好的兼容更多浏览器
// 获取伪类的
var ret2 = window.getComputedStyle(div, '::after').width
var bd = document.querySelector('body') |
【案例:开关灯案例】
【案例:随机背景颜色案例】
【案例:百度换肤】
使用 cssText 可以设置 style 的属性值
<div style="width: 100px; height: 100px">哈哈哈</div> |
节点分类:
元素节点、文本节点、属性节点、注释节点
节点常用的属性
childNodes : 获取所有的子节点
nodeType: 节点类型:元素节点 = 1 属性-2(过时) 注释-8 文本-3
nodeType 链接-MDN
nodeName: 节点名称
nodeValue: 节点值
<ul> |
nextSibling : 下一个兄弟节点 (基本不常用)nextElementSibling : 下一个兄弟元素(IE678 不兼容)previousSibling : 上一个兄弟节点 (基本不常用)previousElementSibling : 上一个兄弟元素 有兼容性问题 可以封装一个兼容性方法<p>导航1</p> |
【案例 1:表单校验】
parentNode : 父节点(没有兼容性问题)parentElement : 父元素div.parentNode // 获取父节点 |
// 作用:在子元素的最后添加一个元素 |
注意 : 如果 newChild 已经存在于 DOM 树中,则它会被从原始位置删除
// 作用:在某个子元素之前添加一个元素 |
- 必须要父节点来调用,newChild 为需要添加的那个节点,refChild 为添加到哪一个节点的前面
- 没有
insertAfter()
// 将元素添加到最后 |
// 语法:var newNode = 节点.cloneNode([isDeep]) |
- 克隆出来的节点跟原来的节点没有关系了,修改了也不会相互影响
- 如果克隆的节点带了 id,我们需要给 id 重新设置一个值,不让 id 冲突
var newNode = div.cloneNode(true) |
可以生成新的节点,但是不推荐使用。如果页面已经加载完成了,再用 document.write 写内容的话,会把之前的页面给覆盖掉
原理:页面从上往下加载的时候,会开启一个文档流,当页面加载完,文档流就会关闭。document.write 的本意就是在文档流上写入内容。如果页面没加载完成,文档流还是开着的,document.write 直接在这个文档流上写东西,如果页面加载完成了,还是用 document.write 写东西,会重新开启一个新的文档流,往新的文档流上写东西,旧的文档流就被新的文档流覆盖了。
window.onload = function() { |
innerHTML 也可以创建节点
使用 innerHTML 创建节点时,如果原来有内容的话,会把原先的内容覆盖
慎用:很容易出现效率问题
div.innerHTML = '<h1>哈哈</h1>' |
// 语法:var element = document.createElement('tagName') |
// 语法:parent.removeChild(child) |
【案例 : 节点操作-删除节点】
1. 使用 children 和 TagName => 需要配合 i-- |
[案例 : 许愿墙案例]
功能1: 克隆10个tip, 并且随机分布 |
// 语法: |
注意 : 如果 newChild 已经存在于 DOM 树中,则它会被从原始位置删除
【动态生成表格】
BOM(Browser Object Model):浏览器对象模型,提供了一套操作浏览器功能的工具

重点 :定时器、 offset 系列
windowwindow.onload 事件会在 窗体加载完成 后执行,通常我们称之为入口函数。
window.onload = function() { |
如果有图片加载,那么代码一定要写到 window.onload 里面,否则会出现图片没有加载完成,获取到的宽度和高度不对的情况。
浏览器会对页面的加载做优化,在加载图片的时候,图片的引入会延迟。
<img src="./01.png" alt=""> |
// 语法:window.open(url, [name], [features]) |
newWin.close() // newWin 是刚刚创建的那个窗口 |
可以在延迟一定时间后执行指定的代码
设置延时器
// 语法: setTimeOut(callback,time) |
清除延时器
// 语法 : clearTimeOut(timerId) |
setInterval 方法重复调用一个函数或执行一个代码段,在每次调用之间具有固定的时间间隔。 (每隔一段时间执行一段代码)
定时器除非清除,否则会一直执行下去。
设置定时器
// 语法 :var timerId = setInterval(func,dealy) |
清除定时器
// 语法 : clearInterval(intervalId) |
【短信验证码案例.html】
location 对象也是 window 的一个属性
location 其实对应的就是浏览器中的地址栏
document.onclick = function() { |
[案例 : 注册成功,3 秒后跳转]
页面跳转:
location.href = 'url地址'
location.assign('url地址')
location.replace('url地址')(不记录历史)
document.onclick = function() { |
location 的其他值
http://www.bbb.com:8080/index.html?id=666&psd=123#xxx
- location.hash //哈希值 其实就是锚点 ==> #xxx
- location.host //服务器 服务器名+端口号 => www.bbb.com:8080
- location.hostname //服务器名 => www.bbb.com
- location.pathname //路径名 => index.html
- location.port //端口 => 8080
- location.protocol //协议 => http
- location.search //参数 => ?id=666&psd=123
// 随便打开一个网页 可以演示 |
console.log(screen.width) // 屏幕的宽度 |
动画公式 :
var step = (target - current) / 10 |
[案例演示 : ]
1. 三步走 |
缺点 : 打开控制台, 查看盒子的结构行内样式 left, 发现并没有跑到 400px, 只能跑到 396.4/395.5
原因 : offsetLeft 获取值的时候, 只会获取整数 , (对小数部分会四舍五入,整数有时候往上取整,有时候往下取整); 可以在获取的 offset 地方打印查看
动画公式 :
var step = (target - current) / 10 |
案例演示 注意点 :
1.查看位置 : left有时候为 395.5/ 396.4 |
动画公式 :
var step = (target - current) / 10 |
案例演示注意点 :
1. 先把盒子 设置 left : 400px 位置; 回到0位置 |
function animate(element, target) { |
[案例 : 筋斗云]
[案例:开机提示关闭]
在触发某个事件的时候,都会产生一个事件对象 Event,这个对象中包含所有与事件相关的一些信息,包括触发事件的元素,事件的类型以及其他与事件相关的信息
鼠标事件触发时,事件对象中会包含鼠标的位置信息。
键盘事件触发时,事件对象中会包含按下的键相关的信息。
现代浏览器获取 : (掌握)
// 给一个形参即可 |
低版本浏览器 (ie678): (了解)
btn.onclick = function() { |
兼容性 :
btn.onclick = function(e) { |
事件对象中有很多很多的属性,但是很多属性并不常用。我们经常用到的是鼠标位置信息和键盘码相关的信息
clientX 与 clientY : 相对于浏览器可视区左上角的位置(不随滚动条滚动而改变)pageX 与 pageY :相对于网页内容(文档 document)左上角的位置screenX 与 screenY :相对于屏幕左上角的位置offsetX 与 offsetY :鼠标相对于事件源左上角的位置document.onmousemove = function(e) { |

[案例 : 拖拽案例]
// 键盘按下的那个键的键盘码 |
onclick、onmouseover 这种 on+事件名称的方式注册事件几乎所有的浏览器都支持
// 注册事件 |
on+事件名称注册事件的缺点:同一个元素同一类型的事件,只能注册一个,如果注册了多个,会出现覆盖问题
现代浏览器支持的注册事件的新方式,这种方式注册的事件不会出现覆盖问题
addEventListener 的语法
// type:事件的类型:click mouseover 字符串类型,不带 on |
removeEventListen 的语法
// type:事件的类型 |
低版本浏览器兼容问题: (了解)
IE678 不支持 addEventListener 与 removeEventListen 两个方法,但是支持 attachEvent 与 detachEvnet
attachEvent 的用法:
// type: 事件类型,需要加上on |
detachEvent 的用法:
detachEvent(type, fn) |
// 添加事件 |
当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为冒泡
说白了就是:当我们触发了子元素的某个事件后,父元素对应的事件也会触发
on 创建的事件默认为冒泡,无法修改
[案例 : 弹窗案例]
阻止事件冒泡 e.stopPropagation()
box.onclick = function(e) { |
event.stopImmediatePropagation
阻止事件冒泡并且阻止相同事件的其他侦听器被调用
如果有多个相同类型事件的事件监听函数绑定到同一个元素,当该类型的事件触发时,它们会按照被添加的顺序执行。如果其中某个监听函数执行了 event.stopImmediatePropagation() 方法,则当前元素剩下的监听函数将不会被执行
阻止事件冒泡的兼容性封装
function stopPropagation(e) { |
事件冒泡是 ie 提出来的
路径 : 触发事件的目标元素(son) > you > father > body > document
事件捕获是火狐提出来的
路径 : document > body > father > you > 触发事件的目标元素 (son)
解析 : 事件的处理将从 DOM 层次的根开始,而不是从触发事件的目标元素开始,事件被从目标元素的所有祖先元素依次往下传递
// 当 addEventListener 第三个参数为 true 时,表示事件捕获 |
事件有三个阶段 :
offset 系列用于用于获取元素自身的大小和位置,在 webapi 中有广泛应用
offset 系列主要有:offsetHeight、offsetWidth、offsetParent、offsetLeft、offsetTop
offsetHeight 与 offsetWidth
style.height 与 style.width
- 只能获取和设置行内样式
- 不包括内边距、边框和外边距
- 获取到的是字符串类型,需要转换
offsetParent
parentNode : 父节点(没有兼容性问题)
parentElement : 父元素
offsetLeft 与 offsetTop
style.left 与 style.top
- 只能获取和设置行内样式
- 获取到的是字符串,需要转换

scroll 系列是用来获取盒子内容的大小和位置
scroll 系列主要有 : scrollWidth、scrollHeight、scrollLeft、scrollTop
scrollWidth 与 scrollHeight
scrollTop
scrollLeft
scrollX 与 scrollY
只读属性
scrollY:文档在垂直方向已滚动的像素值
pageYOffset 属性是 scrollY 属性的别名
为了跨浏览器兼容,请使用 window.pageYOffset 代替 window.scrollY

onscroll 事件
对于有滚动条的盒子,可以使用 onscroll 注册滚动事件,每滚动一像素,就会触发该事件
var div = doucment.getElementById('div') |
场景 : 获取页面被卷去的高度和宽度
通常来说,scroll 家族用的最多的地方就是用来获取页面 被卷去的高度,非常的常用
页面被卷去的高度和宽度的兼容性封装
// 给整个页面注册滚动事件 |
[案例 : 固定导航案例]
clien t 家族用于获取盒子可视区的大小 (内容 + padding)
client 家族有 clientWidth、clientHeight、clientLeft、clientTop
clientWidth、clientHeight 可视区宽高

clientTop 与 clientLeft 完全没有用,他们就是 borderTop 与 borderLeft
onresize 事件:onresize 事件会在窗口被调整大小的时候发生。
window.onresize = function() { |
场景 : client 系列一般用来获取页面的可视区宽高
低版本浏览器 : 获取的 html 和 body
高版本的浏览器 : window.innerWidth (掌握)(只读属性)
// 因为求的是窗口大小所以用 window |
[知乎]我用了两个月的时间才理解 let
[MDN]变量提升
{ |
var a = 1 |
let c = 1 |
let a = 1 |
a = 1; let a // Uncaught ReferenceError: Cannot access 'a' before initialization |
总结:
如果 let x 的初始化过程失败了,那么
const 声明一个常量。常量:代码执行的过程中,不可以修改常量里面的值
const PI = 3.1415 |
const num |
const obj = { name: 'zs' } |
// 定义一个字符串 |
let a = () => {} |
箭头函数的 this、arguments 都是在定义函数时绑定外层的 this 和 arguments,而不是在执行过程中绑定的,所以不会因为调用者不同而发生变化。
可以使用剩余参数(Rest 参数)表示法获得的自身入参列表
因为箭头函数没有 this,因此箭头函数不能作为构造函数
不能用 call()、apply()、bind() 这些方法改变 this 的指向
fn = function(){ |
let fn = (n1, n2) => n1 + n2 |
rest 参数只包括那些没有给出名称的参数,arguments 包含所有参数
rest 参数之后不能再有其他参数,否则会报错
函数的 length 属性,不包括 rest 参数
arguments 对象不是真正的数组,而 rest 参数是数组实例,可以直接使用数组的方法
// 当属性的 key 和变量的名相同时可以简写 |
let propKey = 'foo' |
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes
ES5 中通过 构造函数 + 原型 的方式来实现面向对象
// 构造函数 |
ES6 中出现了 class 关键字,用来实现面向对象
class 声明不允许再次声明已经存在的类,否则将会抛出一个类型错误
class 声明不可以提升
class 仅仅是一个语法结构(语法糖),本质还是函数,实现继承本质上还是通过构造函数 + 原型的方式
class Person {} |
类声明
// 创建 Person 类 |
类的内部所有定义的方法,都是不可枚举的
Object.keys(Person.prototype) // [] |
类表达式
赋予一个命名类表达式的名称是类的主体的本地名称
// 匿名类 |
类表达式也不存在提升
static 关键字用来定义一个类的静态方法。调用静态方法不需要实例化该类,但不能通过一个类实例调用静态方法
class Point { |
继承:要实现至少需要两个 class(子类 和 父类),子类继承自父类,继承后,子类就可以使用父类中的属性或方法
// 继承 |
静态方法 static
静态方法不会被实例继承,而是直接通过类来调用
class Person { |
静态方法可以与非静态方法重名
class Person { |
父类的静态方法,可以被子类继承, 静态方法也是可以从super对象上调用
class Person { |
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)
// 1. 对象解构 |
扩展运算符(spread)是三个点(…)。作用:将一个数组转为用逗号分隔的参数序列
var arr = ['a', 'b', 'c'] |
应用
// 数组深拷贝 |
对象展开
let defaults = { name: 'zs', age: 18 } |
对象展开仅包含对象自身的可枚举属性
class C { |
闭包是函数和声明该函数的词法环境的组合
在 js 中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,产生闭包
产生闭包的条件:有两个函数,是嵌套关系,内部函数引用了外部函数的变量
闭包的作用:
// 闭包的基本模型 |
需求:统计一个函数的调用次数
var count = 0 |
使用闭包解决这个问题
function outer() { |
计算斐波那契数列,会有很大的性能问题,因为重复的计算了很多次,因此我们可以使用缓存来解决这个性能问题。
缺点:既然使用缓存,就需要保证缓存的数据的安全,不能被别人修改,因此,需要使用闭包来实现缓存的私有化。
function outer() { |
正常情况下:函数在调用的时候,去开辟一块内存空间用来执行内部的代码,当函数调用结束的时候,要销毁开辟的空间,节省内存
闭包占用的内存是不会被释放的,因此,如果滥用闭包,会造成内存泄漏的问题。闭包很强大,但是只有在必须使用闭包的时候才使用
内存 中进行的,因此内存的性能对计算机的影响非常大,运行程序需要消耗内存,当程序结束时,内存会得到释放。垃圾回收机器会帮我们回收不再需要使用的内存。引用记数垃圾收集:如果没有引用指向某个对象(或者是函数作用域),那么这个对象或者函数作用域就会被垃圾回收机制回收。
var o = { |
引用计数法无法解决循环引用导致的内存泄露
function fn() { |
使用引用计数法进行垃圾回收的时候,会出现循环引用导致内存泄漏的问题。因此现代的浏览器都采用标记清除法来进行垃圾回收。
这个算法假定设置一个叫做根(root)的对象(在 Javascript 里,根是全局对象 Window)。定期的,垃圾回收器将从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象。
从 2012 年起,所有现代浏览器都使用了标记 - 清除垃圾回收算法。
当闭包的功能不在需要使用了,将这个变量指向 null, 这样闭包占用的内存就可以被回收掉了
function outer() { |
parseInt()函数解析一个字符串参数,指定该字符串为指定基数的进制值,并返回一个 10 进制的整数,如果被解析参数的第一个字符无法被转化成数值类型,则返回NaN
参考 parseInt
parseInt(string, radix)
string 要被解析的值,如果参数不是一个字符串,则将其转换为字符串
radix 基数,表示进制,介于 2 和 36 之间的整数,参数 radix 的值为undefined、0 或没有设置该参数时,parseInt() 会根据 string 来判断数字的基数。如果输入的 string 以 “0x”或 “0x”(一个0,后面是小写或大写的X)开头,那么radix被假定为16,字符串的其余部分被解析为十六进制数。如果输入的 string以 “0”(0)开头, radix被假定为10(十进制)。如果输入的 string 以任何其他值开头, radix 是 10 (十进制)。
parseInt('123', 5) // 将 '123' 看作 5 进制数,返回十进制数 38 |
parseFloat()函数解析一个字符串参数并返回一个浮点数,如果给定值不能被转换成数值,则会返回 NaN
parseFloat('3.14') // 3.14 |
parseInt(0.0000005) === 5 |
typeof // 只能查看基本数据类型的类型 |
typeof
用于查看基本数据的数据类型, number string boolean undefined
null 比较特殊,结果是 object
如果查看复杂数据类型,返回的都是 object 类型
函数的结果是 function
// typeof 判断 |
instanceof 判断
// 语法 |
用来检测 constructor.prototype 是否存在于参数 object 的原型链中
不能用于类型识别
// instanceof 判断 |
constructor.name
Undefined/Null 没有 constructor 属性
var myArr = [] |
Object.prototype.toString
适用于任何类型的检测,不能识别自定义对象类型
Object.prototype.toString.call('str').slice(8, -1) // String |
可使用 continue 跳出当前循环, break 跳出整个循环
如果 for 语句在函数中,使用 return 可以结束 for 循环,同时也会结束函数后续代码的执行
for (var i = 0; i < arr.length; i++) { |
遍历数组
不能用 break continue 语句跳出整个循环
不支持 return 操作输出,return 只用于跳出当前循环
arr.forEach(function(item, index, arr) { |
遍历对象
支持 break, continue 跳出循环
会枚举原型链中的属性
for (var key in obj) { |
如果使用 for in 遍历数组,会产生一些问题
var arr = ['a', 'b', 'c'] |
遍历类数组集合(Array, Map, Set, String, Arguments)
支持 break, continue 和 throw
let arr = [1, 2, 3, 4] |
参考 MDN
无论是 for…in 还是 for…of 语句都是迭代一些东西。它们之间的主要区别在于它们的迭代方式。
for…in 语句以任意顺序迭代对象的可枚举属性。
for…of 语句遍历可迭代对象定义要迭代的数据。
遍历 jQuery 对象集合,为每个匹配的元素执行一个函数
$(selector).each(function(index, element) { |
用来遍历数组(关联数组和索引数组均可)。
foreach($arr as $key => $value) { |
// 遍历关联数组 |
作用域:变量起作用的区域,也就是说:变量定义后,可以在哪个范围内使用该变量
全局作用域 :在 script 标签内,函数外的区域就是全局作用域,在全局作用内声明的变量叫做全局变量 。全局变量可以在任意地方访问。(if/while/for 语句中声明的变量也是全局变量)
函数作用域 :在函数内的区域叫做函数作用域,在函数作用域内声明的变量叫做局部变量 ,局部变量只有在当前函数内才能访问到。
自由变量:对于一个函数来说,函数内部没有声明该变量,但在函数内部有访问该变量。对于这个函数来说, 该变量就是一个自由变量。
隐式全局变量:没有使用 var 定义的变量也是全局变量,叫做隐式全局变量。
var num = 11 |
变量的查找规则:
函数作用域是在函数定义的时候作用域就确定下来了,和函数在哪调用无关
var num = 123 |
作用域链:只要是函数,就会形成一个作用域,如果这个函数被嵌套在其他函数中,那么外部函数也有自己的作用域,这个一直往上到全局环境,就形成了一个作用域链
变量的搜索原则:从当前作用域开始查找,一直查询到全局作用域,如果存在,就返回。如果在全局中也没有找到该变量会报错
// 1. |
预解析过程:js 解析器在执行代码前,会把所有变量的声明和函数的声明提升到当前作用域的顶部。例如var a = 11其实会分为var a 和a = 11两部分,其中var a会被提升
预解析规则:
// 函数预解析 |
不要在一个作用域内重复的声明相同的变量和函数