undefined 是定义了一个变量但没有赋值
null 是定义了一个变量并赋值为null
基本类型 --> String Number Boolean undefined null
引用类型 --> Object function array
值类型 --> 保存的是基本数据类型(值)
引用类型 --> 保存的是引用数据类型(地址值)
两个特殊的:null 和 array
typeof null = 'object'
typeof array = 'object'
其余的数据类型 typeof 都是返回它本身
typeof undefined = 'undefined'
typeof function = 'function'
多个数据的封装体
组成:
属性(字符串 属性值是任意值)
方法(属性值是函数)
数组:arr[索引值]
对象:obj['属性名'] -> [ ]内可以接收变量
属性名包含特殊字符的时候 使用 obj['属性名']
什么是函数?
实现特定功能的 n 条语句的封装体
只有函数是可以执行的
为什么要用函数?
提高代码复用、便于阅读理解
定义函数
1.函数声明
function 函数名(形参){ n 条语句 }
2.表达式
var fn = function(形参){ n 条语句 }
执行函数
1.fn() -> 直接调用 (this:window)
2.obj.fn() -> 以方法形式调用 (this:obj)
3.new Fn() -> 通过new调用 (this:新创建的对象)
4.fn.call/apply(this) -> 指定函数 this 调用
如果不指定this则与直接调用没区别
call方法:
fn.call(指定函数fn的this,实参1,实参2,...)
apply方法:
fn.apply(指定函数fn的this,[实参1,实参2,...])
call指定实参是分开的
apply指定实参是用数组封装起来的
call back
(function(形参){ n 条语句 })()
将匿名函数用()括起来看作一个整体,然后末尾加上()表示调用
作用
()内的是匿名函数是局部作用域,不会污染外部(全局)命名空间
可以不加分号(根据个人喜好)
但是又两个特殊的地方必须加
()前必须加
[]前必须加
var a = 3 ;(function(){})() //立即执行函数
显式原型
每个函数都有一个 prototype 属性 即 显式原型
隐式原型
每个实例对象都有 一个 即 __ proto __ 隐式原型
原型对象其实也是一个实例对象 由 Object 创建的 所以也有__proto__
这个属性
隐式原型对象 指向 缔造者(构造函数)的原型对象
function Fn(){ /* 内部语句:this.prototype = new Object() 等同于:this.prototype = {} prototype对象中的 默认 添加constructor这个属性 */ } var fn = new Fn() // 内部语句 this.\__ proto \__ = Fn.prototype
每个函数都有 显式原型对象 和 隐式原型对象
显式原型对象 .prototype 是由 Object函数创建的
隐式原型对象 .__proto__ 指向这个函数 的 缔造者的 原型对象 Function.prototype
查找变量
变量通过作用域找不到 报错
通过对象.属性 查找一个变量找不到 返回undefined
实例对象.属性 通过原型链找不到这个属性 返回 undefined
1.函数的显式原型指向的对象默认是 Object的实例对象(但Object除外)
原型链终点:Object.prototype.__proto__ === null
Object的原型对象
2.任何函数都是new Function() 创建的(包括Function本身)
所以 任何函数的__proto__
都是Function.prototype
Function.__proto__ === Function.prototype // true
实例对象的 隐式原型对象 等于 构造函数的 显示原型对象
原型链是 .__proto__.__proto__.__proto__ 连起来的一条链子
表达式 A instanceof B
返回一个布尔值
如果函数B的显示原型prototype 在 A实例对象的原型链上,则返回true
var
var 声明变量,会在所有代码执行之前声明(但没赋值)
声明了没赋值 undefined
/* var 没有块级作用域的概念,所有的var变量提前声都是在 作用域的顶部声明。 注意:只有函数才能切割全局作用域 所以 var声明 只有两个情况: 1.在全局作用域的顶部声明 2.在局部作用域的顶部声明 Tips: if 没有切割作用域(只有函数才能切割全局作用域) */
ES6中:let 和 const 才有块级作用域的概念
function
function 函数名(){} 会在所有代码执行之前创建,所以可以在函数声明前调用函数
var 函数名 = function(){} 声明了,但没赋值,不能再声明前调用函数
全局执行上下文
1.所有代码执行之前 将 window 确定为全局执行上下文
2.对全局数据进行预处理
3.开始执行全局代码
函数执行上下文
1.准备调用函数之前,创建对应的函数执行上下文对象
2.对局部数据进行预处理
3.开始执行
4.函数执行完毕释放空间,清除执行上下文
后进先出
window压栈
每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了
全局执行上下文是在全局作用域确定之后,JS 代码执行之前创建的
作用域是静态的,只要函数定义好了就一直是这样不会变化。
执行上下文是动态的,调用函数时创建,函数执行完毕就会自动释放
如何产生?
当一个嵌套的内部函数引用了外部函数的变量时,就产生了闭包
闭包是什么?
理解一:闭包是嵌套的内部函数
理解二:在内部函数中 有一个 包含被引用变量的对象(closure)
我的详细理解
/* 父函数 里面嵌套 子函数 子函数 引用了 父函数的数据 父函数 将 子函数返回return,一个变量接收父函数的返回值 通过这个变量可以调用子函数,但是调用子函数 必须用到 父函数的数据 父函数 return 之后 代表已经执行完毕。 正常情况:一个函数执行完毕会释放内存空间,清除执行上下文。 关键是 子函数 还要用到 父函数的数据 所以 父函数 return之后 数据还存在于内存中 没有被清除,这就形成了闭包 */
产生条件
闭包次数问题
外部函数被调用了几次就产生了几个闭包
1.将内部函数作为返回值返回
2.内部函数作为实参传递给另一函数调用
1.使函数内部的变量在函数执行完毕之后,仍然存活在内存中
2.让函数外部可以操作到函数内部的数据
产生:内部嵌套的函数 定义执行完毕时就产生了
死亡:内部嵌套的函数 称为垃圾对象时(垃圾对象会被浏览器垃圾回收机制清除)
定义JS模块
1.函数执行完毕之后,函数内的局部变量没有释放,占用内存
2.容易造成内存泄露
解决:能不用闭包就不用,及时释放(= null)
内存溢出
程序运行占用内存过大,导致溢出,会报错
内存泄露
Object 构造函数
对象字面量
工厂模式
自定义构造函数
构造函数与原型组合
子类型的原型对象 为 父类型 的 实例对象
使 子类型的原型对象.__proto__ === 父类型.prototype
原本 子类型的原型对象.__proto__ === Object.prototype
子类型创建的实例对象 的 原型链上就可以找到 父类型的原型对象
可以调用夫类型原型对象身上的属性或方法
/* A是父类型构造函数 实例对象 a B是父类型构造函数 实例对象 b b.__proto__ === B.protoype B.prototype.__proto__ === Object.prototype ` <!-- 关键: 使子类型的原型对象 为 父类型的 实例对象 -->` B.prototype = new A() `<!-- 修正constructor属性,要不然就找到父类型的原型对象.constructor -->` B.prototype.constructor = B `<!-- 这时 B.prototype 就是 A的 实例对象-->` B.prototype.__proto__ === A.prototype b 就可以通过原型链找到 A.prototype ,能找到就能使用 A 原型身上的属性或方法 但 b 是 new B() 创建的 这就让 B 继承了 A */
这里没弄太明白
进程和线程
多进程:一个应用程序可以同时启动多个实例运行
多线程:在一个进程内,同时由多个线程运行
分线程
var worker = new Worker('worker.js')
不常用原因:
1.慢
2.不能跨域加载JS
3.worker 内代码不能访问 DOM 更新页面,因为全局作用域看不到window和document
4.不是每个浏览器都兼容