JS高级笔记

JS高级


一、基础总结

typeof: 返回的是数据类型的字符串表达形式

1.基本类型

类型判断
Numbertypeof
Stringtypeof
Booleantypeof
undefinedtypeof/===
null===

2.对象类型

对象判断
Objecttypeof/instanceof
Arrayinstanceof
Functiontypeof(特别的对象,可执行)

3.数据,变量,内存的理解

  1. 什么是数据?
  • 在内存中可读的, 可传递的保存了特定信息的’东东’
  • 一切皆数据, 函数也是数据
  • 在内存中的所有操作的目标: 数据
  • 数据对象
    • 基本类型
    • 对象类型
  1. 什么是变量?
  • 在程序运行过程中它的值是允许改变的量
  • 一个变量对应一块小内存, 它的值保存在此内存中
  • 变量类型:
    • 基本类型: 保存基本类型数据的变量
    • 引用类型: 保存对象地址值的变量
  1. 什么是内存?
  • 内存条通电后产生的存储空间(临时的)
  • 一块内存包含2个方面的数据
    • 内部存储的数据
    • 地址值数据
  • 内存空间的分类
    • 栈空间: 全局变量和局部变量
    • 堆空间: 对象
  1. 内存,数据, 变量三者之间的关系
  • 内存是容器, 用来存储不同数据
  • 变量是内存的标识, 通过变量我们可以操作(读/写)内存中的数据

4.关于引用变量赋值问题

  • 2个引用变量指向同一个对象, 通过一个引用变量修改对象内部数据, 另一个引用变量也看得见
  • 2个引用变量指向同一个对象,让一个引用变量指向另一个对象, 另一个引用变量还是指向原来的对象
//1.
  var obj1 = {}
  var obj2 = obj1
  obj2.name = 'Tom'
  console.log(obj1.name)//Tom
  function f1(obj) {
    obj.age = 12
  }
  f1(obj2)
  console.log(obj1.age)//12

//2.
  var obj3 = {name: 'Tom'}
  var obj4 = obj3
  obj3 = {name: 'JACK'}
  console.log(obj4.name)//Tom
  function f2(obj) {
    obj = {name: 'Bob'}
  }
  f2(obj4)
  console.log(obj4.name)//Tom

5.对象的理解和使用

  1. 什么是对象?
  • 多个数据(属性)的集合
  • 用来保存多个数据(属性)的容器
  1. 属性的分类:
  • 一般 : 属性值不是function 描述对象的状态
  • 方法 : 属性值为function的属性 描述对象的行为
  1. 方法
  • 对象.属性名
  • 对象[‘属性名’]: 属性名有特殊字符/属性名是一个变量

6.函数的理解和使用

  • 什么是函数?

    • 用来实现特定功能的, n条语句的封装体
    • 只有函数类型的数据是可以执行的, 其它的都不可以
  • 为什么要用函数?

    • 提高复用性
    • 便于阅读交流
  • 函数也是对象

    • instanceof Object===true
    • 函数有属性: prototype
    • 函数有方法: call()/apply()
    • 可以添加新的属性/方法
  • 函数的3种不同角色

    • 一般函数 : 直接调用
    • 构造函数 : 通过new调用
    • 对象 : 通过.调用内部的属性/方法
  • 函数中的this

    • 显式指定谁: obj.xxx()
    • 通过call/apply指定谁调用: xxx.call(obj)
    • 不指定谁调用: window
    • 回调函数: 看背后是通过谁来调用的: window/其它
  • 匿名函数自调用:IIFE立即执行函数:

    (function(){
      //实现代码
    })()
    
  • 回调函数的理解
    像这种函数,由我们创建但是不由我们调用的,我们称为回调函数

    • 常用的回调函数
      • dom事件回调函数
      • 定时器回调函数
      • ajax请求回调函数
      • 生命周期回调函数

二、函数高级

1.原型与原型链

1.原型

  • 所有函数都有一个特别的属性:
    • prototype : 显式原型属性,它是一个普通的对象
  • 所有实例对象都有一个特别的属性:
    • __proto__ : 隐式原型属性,它也是一个普通的对象
  • 所有引用类型,它的’_ _ proto_ _'属性指向它的构造函数的’prototype’属性
function Fn() {
  }
  var fn = new Fn()
  console.log(Fn.prototype===fn.__proto__)//true
  • 当试图得到一个对象的属性时,如果这个对象本身不存在这个属性,那么就会去它的’_ _ proto_ _'属性(也就是它的构造函数的’prototype’属性)中去寻找

2.原型链

  • 访问一个对象的属性时,

    • 先在自身属性中查找,找到返回
    • 如果没有, 再沿着__proto__这条链向上查找, 找到返回
    • 如果最终没找到, 返回undefined
      哈哈
  • 判断:

    • 表达式: A instanceof B
    • 如果B函数的显式原型对象在A对象的原型链上, 返回true, 否则返回false
  • 属性问题:

    • 读取对象的属性值时: 会自动到原型链中查找
    • 设置对象的属性值时: 不会查找原型链, 如果当前对象中没有此属性, 直接添加此属性并设置其值
    • 方法一般定义在原型中, 属性一般通过构造函数定义在对象本身上

2.执行上下文

1.变量提升与函数提升

  • 变量声明提升
    • 通过var定义(声明)的变量, 在定义语句之前就可以访问到
    • 值: undefined
  • 函数声明提升
    • 通过function声明的函数, 在之前就可以直接调用
    • 值: 函数定义(对象)
    • 通过var xxx=function定义的函数,无函数提升
  • 先有变量提升, 再有函数提升

2.执行上下文

函数执行上下文环境是在调用函数时, 函数体代码执行之前创建
全局执行上下文环境是在全局作用域确定之后, js代码马上执行之前创建

  1. 代码分类(位置)
    全局代码
    函数(局部)代码
  2. 全局执行上下文
    在执行全局代码前将window确定为全局执行上下文
    对全局数据进行预处理
    • var定义的全局变量==>undefined, 添加为window的属性
    • function声明的全局函数==>赋值(fun), 添加为window的方法
    • this==>window
    • 开始执行全局代码
  3. 函数执行上下文
    在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象
    对局部数据进行预处理
    • 形参变量==>赋值(实参)==>添加为执行上下文的属性
    • arguments==>赋值(实参列表), 添加为执行上下文的属性
    • var定义的局部变量==>undefined, 添加为执行上下文的属性
    • function声明的函数 ==>赋值(fun), 添加为执行上下文的方法
    • this==>赋值(调用函数的对象)
  • 开始执行函数体代码

3.执行栈

可以参考博客

4.作用域与作用域链

  • 1.分类
    • 全局作用域
    • 函数作用域
    • 没有块作用域(ES6有了)
    1. 作用
    • 隔离变量,不同作用域下同名变量不会有冲突
    • 多个上下级关系的作用域形成的链, 它的方向是从内到外
    • 查找变量时就是沿着作用域链来查找的

5.闭包

1.
  • 理解:函数嵌套函数,内部函数可以引用外部函数的参数和变量,参数和变量不会被垃圾回收机制收回

  • 产生闭包的条件:

    • 函数嵌套
    • 内部函数引用了外部函数的数据(变量/函数)
    • 执行函数
  • 常见的闭包

    • 将函数作为另一个函数的返回值
    function fn1() {
     var a = 2
     function fn2() {
       console.log(a)
     }
     return fn2
    }
    fn1()
    
    • 将函数作为实参传递给另一个函数调用
2.作用

1.闭包拥有全局变量的不被释放的特点

2.闭包拥有局部变量的无法被外部访问的特点

3.可以让一个变量长期在内存中不被释放

4.避免全局变量的污染,和全局变量不同,闭包中的变量无法被外部使用

5.私有成员的存在,无法被外部调用,只能直接内部调用

3.生命周期
  1. 产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)
  2. 死亡: 在嵌套的内部函数成为垃圾对象时
4.应用

定义JS模块

  • 具有特定功能的js文件
  • 将所有的数据和功能都封装在一个函数内部(私有的)
  • 只向外暴露一个包信n个方法的对象或函数
  • 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能

例如:

/**
 * 自定义模块1
 */
function coolModule() {
  //私有的数据
  var msg = 'abc'

  //私有的操作数据的函数
  function doSomething() {
    console.log(msg)
  }
  function doOtherthing() {
    console.log(msg)
  }

  //向外暴露包含多个方法的对象
  return {
    doSomething: doSomething,
    doOtherthing: doOtherthing
  }
}


/**
 * 自定义模块2
 */
(function (window) {
  //私有的数据
  var msg = 'abc'
  var names = ['I', 'Love', 'you']
  //操作数据的函数
  function a() {
    console.log(msg)
  }
  function b() {
    console.log(names.join(' '))
  }
//向外暴露包含多个方法的对象
  window.coolModule2 =  {
    doSomething: a,
    doOtherthing: b
  }
})(window)
5.缺点:内存溢出与内存泄露
  • 缺点

    • 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长
    • 容易造成内存泄露
  • 内存溢出

    • 一种程序运行出现的错误
    • 当程序运行需要的内存超过了剩余的内存时, 就出抛出内存溢出的错误
  • 内存泄露

    • 占用的内存没有及时释放
    • 内存泄露积累多了就容易导致内存溢出
    • 常见的内存泄露:
      • 意外的全局变量
      • 没有及时清理的计时器或回调函数
      • 闭包

三、对象高级

1.对象创建模式

  • 方式一: Object构造函数模式
    • 方法: 先创建空Object对象, 再动态添加属性/方法
    • 适用场景: 起始时不确定对象内部数据
    • 问题: 语句太多
  var p = new Object()
  p = {}
  p.name = 'Tom'
  • 方式二: 对象字面量模式
    • 方法: 使用{}创建对象, 同时指定属性/方法
    • 适用场景: 起始时对象内部数据是确定的
    • 问题: 如果创建多个对象, 有重复代码
var p = {
    name: 'Tom',
    setName: function (name) {
      this.name = name
    }
  }
  • 方式三: 工厂模式
    • 方法: 通过工厂函数动态创建对象并返回
    • 适用场景: 需要创建多个对象
    • 问题: 对象没有一个具体的类型, 都是Object类型
function createPerson(name, age) {
    var p = {
      name: name,
      age: age,
      setName: function (name) {
        this.name = name
      }
    }
    return p
  }

var p1 = createPerson(‘Tom’, 12)
var p2 = createPerson(‘JAck’, 13)

  • 方式四: 自定义构造函数模式
    • 方法: 自定义构造函数, 通过new创建对象
    • 适用场景: 需要创建多个类型确定的对象
    • 问题: 每个对象都有相同的数据, 浪费内存
 function Person(name, age) {
    this.name = name
    this.age = age
    this.setName = function (name) {
      this.name = name
    }
  }

  var p1 = new Person('Tom', 12)
  var p2 = new Person('Tom2', 13)
  • 方式五: 构造函数+原型的组合模式
    • 方法: 自定义构造函数, 属性在函数中初始化, 方法添加到原型上
    • 适用场景: 需要创建多个类型确定的对象
 function Person (name, age) {
    this.name = name
    this.age = age
  }
  Person.prototype.setName = function (name) {
    this.name = name
  }
  var p1 = new Person('Tom', 12)
  p1.setName('TOM')

2.继承

方式1: 原型链继承

 function Supper() { //父类型
    this.superProp = 'The super prop'
  }
  //原型的数据所有的实例对象都可见
  Supper.prototype.showSupperProp = function () {
    console.log(this.superProp)
  }

  function Sub() { //子类型
    this.subProp = 'The sub prop'
  }

  // 子类的原型为父类的实例
  Sub.prototype = new Supper()
  // 修正Sub.prototype.constructor为Sub本身
  console.log(Sub.prototype.constructor == Supper)//true

  Sub.prototype.showSubProp = function () {
    console.log(this.subProp)
  }

  // 创建子类型的实例
  var sub = new Sub()
  // 调用父类型的方法
  sub.showSupperProp()
  // 调用子类型的方法
  sub.showSubProp()

方式2: 借用构造函数继承

function Person(name, age) {
    this.name = name
    this.age = age
  }

  function Student(name, age) {
    Person.call(this, name, age)   // this.Person(name, age)
  }

  var s = new Student('Tom', 20)
  console.log(s.name, s.age)

方式3: 原型链+借用构造函数的组合继承

  1. 利用原型链实现对父类型对象的方法继承
  2. 利用call()借用父类型构建函数初始化相同属性
function Person(name, age) {
    this.name = name
    this.age = age
  }
  Person.prototype.setName = function (name) {
    this.name = name
  }

  function Student(name, age, price) {
    Person.call(this, name, age) //得到父类型的属性
    this.price = price
  }
  Student.prototype = new Person()  //得到父类型的方法
  Student.prototype.constructor = Student
  Student.prototype.setPrice = function (price) {
    this.price = price
  }

  var s = new Student('Tom', 12, 10000)
  s.setPrice(11000)
  s.setName('Bob')
  console.log(s)
  console.log(s.constructor)

四、线程机制与事件机制

1.线程与进程

  • 进程:
    • 程序的一次执行, 它占有一片独有的内存空间
    • 可以通过windows任务管理器查看进程
  • 线程:
    • 是进程内的一个独立执行单元
    • 是程序执行的一个完整流程
    • 是CPU的最小的调度单元
  • 关系
    • 一个进程至少有一个线程(主)
    • 程序是在某个进程中的某个线程执行的

2.浏览器内核模块组成

  • 主线程
    • js引擎模块 : 负责js程序的编译与运行
    • html,css文档解析模块 : 负责页面文本的解析
    • DOM/CSS模块 : 负责dom/css在内存中的相关处理
    • 布局和渲染模块 : 负责页面的布局和效果的绘制(内存中的对象)
  • 分线程
    • 定时器模块 : 负责定时器的管理
    • DOM事件模块 : 负责事件的管理
    • 网络请求模块 : 负责Ajax请求

3. js线程

  • JavaScript的单线程,与它的用途有关。
  • 作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。
  • 这决定了它只能是单线程,否则会带来很复杂的同步问题
  • js是单线程执行的(回调函数也是在主线程)
  • H5提出了实现多线程的方案: Web Workers
  • 只能是主线程更新界面

4.定时器问题:

  • 定时器并不真正完全定时
  • 如果在主线程执行了一个长时间的操作, 可能导致延时才处理

5.事件处理机制

  • 代码分类
    • 初始化执行代码: 包含绑定dom事件监听, 设置定时器, 发送ajax请求的代码
    • 回调执行代码: 处理回调逻辑
  • js引擎执行代码的基本流程:
    • 初始化代码===>回调代码
  • 模型的2个重要组成部分:
    • 事件管理模块
    • 回调队列
  • 模型的运转流程
    • 执行初始化代码, 将事件回调函数交给对应模块管理
    • 当事件发生时, 管理模块会将回调函数及其数据添加到回调列队中
    • 只有当初始化代码执行完后(可能要一定时间), 才会遍历读取回调队列中的回调函数执行

6.H5 Web Workers

  • 可以让js在分线程执行
  • Worker
    var worker = new Worker('worker.js');
    worker.onMessage = function(event){event.data} : 用来接收另一个线程发送过来的数据的回调
    worker.postMessage(data1) : 向另一个线程发送数据
    
  • 问题:
    • worker内代码不能操作DOM更新UI
    • 不是每个浏览器都支持这个新特性
    • 不能跨域加载JS

总结

以上就是JS高级的笔记

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值