JavaScript高级知识整理

1 基础总结

01 数据类型
基本类型:
  • String: 任意的字符串
  • Number: 任意的数字
  • boolean: true / false
  • undefined: undefined
  • null: null
  • Symbol: 不能当做构造函数使用,不能使用 new 关键字。可以用来表示对象的唯一键值
  • BigInt: 不能当做构造函数使用,不能使用 new 关键字。可以表示任意大的整数。
对象类型
  • Object: 任意的对象
  • Array: 一种特殊的对象 (内部数据是有序的)
  • function: 一种特殊的对象 (可以执行)
判断:
  • typeof
  • instanceof
  • === : 全等于 / 注意尽量不要用 == 因为会做数据转换
02 undefined 与 null
  1. undefined 与 null 的区别?
    undefined: 是定义了但没赋值
    null: 定义了并赋值为 null
  2. 什么时候会给变量赋值为null?
    初始赋值时, 表明将要为变量 a 赋值为对象 (相当于是为对象占位)
    结束前, 将变量 a 赋值为 null 使之前对象失去引用变为垃圾对象 (被处理机制回收)
03 变量与内存
  1. 问题: var a = xxx , a内存中保存的是什么?
    - 如果 xxx 是基本数据 如 3 , a内存中保存的是这个基本数据 3
    - 如果 xxx 是对象数据 , a内存中保存的是那个对象的地址值
04 对象
  1. 什么是对象?
    对象是多个数据的封装体
    一个对象代表现实中的一个事物
  2. 为什么要用对象?
    统一管理多个数据
  3. 对象的组成?
    - 属性: 属性名(字符串)和属性值(任意)组成
    - 方法: 一种特别的属性(属性值是函数)
  4. 如何访问对象内部的数据?
    - .属性名: 编码简单, 有事不能用
    - ['属性名']: 编码麻烦, 能通用
  var p = {
    'name': 'Tom',
    age: 13,
    setName: function (name) {
      this.name = name
    },
    setAge: function (age) {
      this.age = age
    }
  }

  p.setName('Amy')
  console.log(p.age, p['age'], p['name']); // 13 13 "Amy"
  1. 什么时候必须使用 ['属性名'] 的形式?
    1. 属性名包含特殊字符时: - 空格
    - 给对象p添加一个属性: content type: text/json 此时p.content type = 'text/json' //不可用
    2. 属性名不确定时
 // 属性名包含特殊字符时: -  空格
  var p = {
    'content type': 'text/json'
  }
  console.log(p['content type']); // 'text/json'

  // 属性名不确定时
  var proName = 'myAge'
  var value = 18
  //此时 p.proName = value //不能用 用了p中键值对为: 'proName': 18
  p[proName] = value // 注意是proName变量 而不是字符串
  console.log(p.[proName]); //
05 函数
01 回调函数
  1. 什么叫回调函数?
    - 定义了 但没有调用 他自己执行了
  2. 常见的回调函数有哪些?
    - Dom事件回调函数
    - 定时器回调函数
    - ajax 请求回调函数
    - 生命周期回调函数
  document.getElementById('btn').onclick = function () {
    alert(this.innerHTML)
  }

  setTimeout(function () {
    alert('到点了')
  }, 2000)
02 匿名函数自调用

作用:
- 隐藏实现
- 不会污染外部(全局)的命名空间

  // 简单实现
  var a = 18
  ;(function () {
    var a = 14
    console.log(a);
  })()
  console.log(a)

  ;(function () {
    var age = 17
    function test () {
      console.log(age);
    }
    window.$ = function () {
      return {
        test: test
      }
    }
  })()

  $().test()
  console.log($());

此处 $ 是个函数 , $() 为执行 结果得到一个 对象 {} , 对象中拥有 test 方法

03 函数中的 this
  1. this是什么?
    - 任何函数的本质上都是通过某个对象来调用的, 如果没有直接指定那this就是 window
    - 所有的函数内部都有一个变量 this
    - 值是调用函数的当前对象
  2. 如何确定this的值?
    - test(): window
    - p.test(): p
    - var p = new test(): p
    - p.call(obj): obj
  function Person (color) {
    console.log(this)
    this.getColor = function () {
      console.log(this)
    }
  }

  Person('red') // this: window

  var p = new Person("yello") // this: p

  var obj = {}
  p.getColor.call(obj, "pink") // this: obj

  var test = p.getColor
  test() // this: window

  function fun1() {
    console.log(this)
    function fun2() {
      console.log(this)
    }
    fun2() // this: window
  }
  fun1() // this: window

2 函数高级

01 原型与原型链

01 原型 prototype
  1. 函数的prototype属性
    - 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
    - 原型对象中有一个属性constructor, 它指向函数对象
  2. 给原型对象添加属性(一般是方法)
    - 作用: 函数的所有实例对象自动拥有原形中的属性(方法)
  // 每个函数都有一个prototype属性, 它默认默认指向一个Object空对象(即称为: 原型对象)
  console.log(Date.prototype)
  function Fun() {}
  console.log(Fun.prototype) // 默认指向一个空的对象(没有我们自己的属性)

  // 原型对象中有一个属性constructor, 它指向函数对象
  console.log(Fun.prototype.constructor === Fun)
  console.log(Date.prototype.constructor === Date)

  // 给原型对象添加属性(一般是方法) ====> 实例对象可以访问
  Fun.prototype.test = function () {
    console.log('test')
  }
  var fun = new Fun()
  fun.test()
02 显式原型与隐式原型

1. 每个函数function都有一个prototype, 即显式原型(属性)
2. 每个实例对象都有一个__proto__, 即隐式原型(属性)
3. 实例对象的隐式原型的值等于构造函数的显式原型的值, 即 prototype 与 __proto__地址值相同
4. 函数的prototype属性: 在定义函数时自动添加的, 默认值为一个空的Object对象的地址值
5. 实例对象的__proto__属性: 创建实例对象时自动添加的, 默认值为构造函数的prototype属性值
画图:
在这里插入图片描述

03 原型链
  1. 原型链 别名:(隐式原型链), 作用: 查找对象的属性或方法
  2. 访问一个对象的属性时:
    - 先在自身属性查找, 找到返回
    - 如果没有,再沿着__proto__这条链找, 找到返回
    - 如果最终找不到, 返回undefined
    画图:
    在这里插入图片描述
04 构造函数 原型对象与 实例对象关系图

在这里插入图片描述

05 面试题

在这里插入图片描述

02 变量提升与函数提升

在 JavaScript 中函数是一等公民, 函数提升的优先级比变量提升的优先级高

  1. 变量提升
    - 通过var定义(声明)的变量, 在定义语句之前就可以访问到
    - 值: undefined
  2. 函数提升
    - 通过function声明的函数, 在之前就可以直接调用
    - 值: 函数对象
  /**
   *  面试题
   * */
  var a = 3
  function fn() {
    console.log(a); // 变量提升  undefined
    var a = 4
  }

  fn()

  xxx() // 可调用 函数提升

  function xxx() {
    console.log('你好');
  }

  obj() // 不能调用 变量提升 值: undefined // obj is not a function

  var obj = function () {
    console.log('obj');
  }

03 执行上下文与执行上下文栈

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

  var a1 = 3
  function a2() {
    console.log('a2()');
  }
  /*函数执行上下文*/
  function fn(a1) {
    console.log(a1)   // 2
    console.log(a2)   // undefined
    a3()              // 'a3()'
    console.log(this) // window
    console.log(arguments)  // [2, 3] 伪数组 : 参数列表

    var a2 = 3
    function a3() {
      console.log('a3()');
    }
  }
  fn(2, 3, 4)
02 执行上下文栈

1. 在全局代码执行前, JS引擎就会创建一个栈来存储管理所有的执行上下文对象
2. 在全局执行上下文( window )确定后, 将其压入栈中(入栈)
3. 在函数执行上下文创建后将其压入栈中(入栈)
4. 在当前函数执行完成后将栈顶的上下文对象移除(出栈)
5. 当所有的代码执行完成后, 栈中只剩下 window
在这里插入图片描述

03 面试题
// 上方函数解析之后得到下方函数
  console.log('最开始 i 值:' + i) // undefined
  var i = 1
  foo(i)
  function foo(i) {
    if (i === 4) {
      return
    }
    console.log('foo()调用前 i 值:' + i)
    foo(i + 1) // 递归调用: 在函数内部自己调用自己
    console.log('foo()调用后 i 值:' + i)
  }
-------------------------------------------------------------
function foo(i) {
    if (i === 4) {
      return
    }
    console.log('foo()调用前 i 值:' + i)		// 1
    // foo(i + 1) // 递归调用: 在函数内部自己调用自己
    function foo(2) {
      if (2 === 4) {
        return
      }
      console.log('foo()调用前 i 值:' + i)	// 2
      // foo(2 + 1) // 递归调用: 在函数内部自己调用自己
      function foo(3) {
        if (3 === 4) {
          return
        }
        console.log('foo()调用前 i 值:' + i)		// 3
        // foo(3 + 1) // 递归调用: 在函数内部自己调用自己
        function foo(4) {
          if (4 === 4) {
            return
          }
        }
        console.log('foo()调用后 i 值:' + i) // 3
      }
      console.log('foo()调用后 i 值:' + i) // 2
    }
    console.log('foo()调用后 i 值:' + i) // 1
  }
	// JavaScript中 函数是一等公民 先执行函数提升 然后执行 变量提升
    // 面试题 1
    console.log(typeof a) // function
    function a() {}
    var a 
    // 面试题 2
    console.log(typeof a) // function
    function a() {}
    var a = 1
    // 面试题 3
    function a() {}
    var a = 1
    console.log(typeof a) // number

  /*
    面试题 4
  * */
  if (!(b in window)) { // === var b 
    var b = 1
  }
  console.log(b) // undefined

  /*
    面试题 5
  * */
  var c = 1
  function c(c) {
    console.log(c)
  }
  c(2) // c is not a function
  /*
    上式等价于
  */
  var c
  function c(c) {
    console.log(c)
  }
  c = 1
  c()
04 作用域面试题

作用域分为: 全局作用域 和 函数作用域
在这里插入图片描述
找不到就会向上一层查找, 所以 x 值为: 10
在这里插入图片描述

  var fn = function() {
    console.log(fn) // ƒ () { console.log(fn) }
  }
  fn()

  var obj = {
    fn2: function() {
      // console.log(fn2)
      console.log(this.fn2)
      console.log(obj.fn2)
      console.log(window.obj.fn2)
    }
  }
  obj.fn2()

04 闭包

  1. 如何产生闭包?
    - 当一个内部子函数引用了外部父函数的变量(函数)时, 就产生了闭包
  2. 闭包到底是什么?
    - 使用chrome调试查看: ==>Sources ==>打断点 ==>Scope ==>Local ==>[[Scopes]] ==>Closure(闭包)
    - 理解一: 闭包是嵌套的内部函数(绝大数人)
    - 理解二: 闭包是包含被引用变量(函数)的对象(极少数人)
    - 注意: 闭包存在于嵌套的内部函数中
  3. 产生闭包的条件?
    - 函数嵌套
    - 内部函数引用了外部函数的数据(变量/函数)
  4. 闭包的作用
    - 使用闭包函数执行完成后,内部函数引用外部函数的数据(变量/函数),还存在于内存中
    - 使函数外部可以操作(读写)到函数内部的数据(变量/函数)
  5. 闭包的生命周期
    1. 产生: 在内部嵌套函数定义执行(不是真实执行)完成时就产生了(不是在调用)
    2. 死亡: 在内部嵌套函数成为垃圾对象时
  function fn1() {
// 程序执行到此处时, 先函数提升然后变量提升(内部函数对象创建) 闭包出现 
// 因为有 var a = undefined
    var a = 2
    function fun() {
      console.log(++a)
    }
    return fun
  }
  var f = fn1()
  f() // 3
  f() // 4
  // 此时没有死亡
  f = null // 此时内部函数无引用指向 死亡
  1. 闭包的应用_定义js模块
    - 具有特定功能的js文件
    - 将所有的数据和方法都封装到一个函数的内部(私有的)
    - 只向外部暴露一个包含n个方法的对象或函数
    - 模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能
my_module.js模块 方法二更好*
/*方法一*/
function my_module() {
  var msg = 'songRuiXue'
  function doSomething() {
    console.log('doSomething' + msg.toUpperCase()) // msg: 全部大写
  }
  function doOtherthing() {
    console.log('doOtherthing' + msg.toLowerCase()) // msg: 全部小写
  }
  return {
    doSomething: doSomething,
    doOtherthing: doOtherthing
  }
}
/*方法二*/
(function(window) {
  var msg = 'songRuiXue'
  function doSomething() {
    console.log('doSomething' + msg.toUpperCase()) // msg: 全部大写
  }
  function doOtherthing() {
    console.log('doOtherthing' + msg.toLowerCase()) // msg: 全部小写
  }
  window.obj = {
    doSomething: doSomething,
    doOtherthing: doOtherthing
  }
})(window)
调用
  /*方法一*/
  var obj = my_module()
  obj.doSomething()
  obj.doOtherthing()

  /*方法二*/
  obj.doSomething()
  obj.doOtherthing()
  1. 内存溢出与内存泄漏
    • 内存溢出
      - 一种程序运行时出现的错误
      - 当程序运行需要的内存超过了剩余的内存时, 就会抛出内存溢出的错误
    • 内存泄漏
      - 占用的内存没有及时的释放
      - 内存泄漏过多就容易导致内存溢出
      - 常见的内存泄漏:
      1. 意外的全局变量(没有用var定义的变量)
      2. 没有及时清理的定时器或回调函数
      3. 闭包
      例子
  /*意外的全局变量*/
  function fn() {
    a = new Array(100000)
    *a没有用var定义*
    console.log(a)
  }
  fn()
  
  /*开启循环定时器后不清理*/
  var intervalId = setInterval(function() {
    console.log('1111111111')
  }, 2000)
  // clearInterval(intervalId)  清理循环定时器
  
  /*闭包*/
  function fn1() {
    var a = 4
    function fn2() {
      console.log(++a)
    }
    return fn2
  }
  var f = fn1()
  f()
  // f = null 使fn2函数失去引用成为垃圾对象

面试题
在这里插入图片描述

05 对象高级

01 对象的创建模式
  • Object构造函数模式
    var obj = new Object() // var obj = {}
    obj.name = 'Tom'
    obj.setAge = function(age) {
       this.age = age // this 指向 obj
    }
    
    obj.setAge(12)
    console.log(obj);
    
  • 对象字面量模式
    var obj = {
        name: 'Tom',
        age: 12,
        setName: function (name) {
          this.name = name // obj调用方法 所以 this 指向 obj
        }
    }
    obj.setName('Amy')
    console.log(obj);
    
  • 构造函数模式
    function Person(name, age) {
      this.name = name // this 指向 new 出来的实例对象
      this.age = age
      this.setName = function(name) {
         this.name = name // this 指向 new 出来的实例对象
      }
    }
    var p = new Person('Tom', 12)
    console.log(p);
    
  • 构造函数+原型的组合模式
    • 将实例对象共有的方法或属性添加到构造函数的原型对象上( 优化代码 )
    function Person(name, age) {
    	this.name = name
    	this.age = age // this 指向 new 出来的实例对象
    }
    Person.prototype.setName = function(name) {
    	this.name = name // this 指向 new 出来的实例对象
    }
    var p = new Person('Tom', 12)
    p.setName('Amy')
    console.log(p);
    
02 js中的继承

在ES6之前, 需要借助于构造函数+原型对象实现继承, 现在我们可以利用ES6的extends方法实现继承

01 call函数的使用
fun.call(this, name, age, price)
  • this: 指向的是当前调用函数的对象
  • name, age, price: 传入的是一个参数列表,而不是单个数组。
  function son() {
    console.log(this) // this 指向 window
  }
  son.call() // this 指向 window

  var obj = {}
  son.call(obj) // this 指向 obj
02 借用构造函数继承父类型属性

核心思想:通过call()把父类型的this指向子类型的this,这样就可以实现子类型继承父类型的属性。

// 借用父构造函数继承属性
//父构造函数
function Father (name, age) {
  //this指向父构造函数的对象实例
	this.name = name
  	this.age = age
}
// 子构造函数
function Son (name, age){
  // this指向子构造函数的实例对象
  //借助于call,this指向子构造函数实例对象 new Son('Amy',18)
  Father.call(this, name, age)
}
var son = new Son('Amy'18)
console.log(son)
3 借用原型对象继承父类型方法
// 父构造函数
function Father (uname, age) {
  //this指向父构造函数的对象实例
	this.uname = uname
  this.age = age
}
// 父原型方法
Father.prototype.money = function() {
	console.log(10000)
}
// 子构造函数
function Son (uname, age){
  // this指向子构造函数的实例对象
  //借助于call,this指向子构造函数的实例对象
  Father.call(this, uname, age)
  
}

// Son.prototype = Father.prototype 这样直接赋值会有问题,如果修改了子原型对象,
//父原型对象也会变化

// 将父级构造函数的实例对象赋值给子级的原型
Son.prototype = new Father()

//手动改constructor指回原来的构造函数
Son.prototype.constructor = Son  
Son.prototype.get= function() {
 console.log('----')
}
var son = new Son('Amy'18)
console.log(son) 

06 函数重载

重载(overload)
什么是: 多个同名的函数,但是形参列表不同,在调用时,可自动根据传入实参列表的不同,选择匹配的函数执行

问题: js不支持其他语言那种重载的语法: 因为js不允许多个同名函数同时存在!如果同时定义多个同名函数,则最后一个同名函数,会覆盖之前所有同名函数!
解决: 借助于arguments对象 来变通实现, 只定义一个函数! 且不要定义形参! 每个函数内都有一个arguments对象:
什么是arguments: 专门接受传入函数的所有实参值的类数组对象(长得像数组的对象)
相同: 下标, length 不同: 不是数组类型,而是对象类型,所以类数组对象无法使用数组类型的函数!
何时使用: 今后在js中,如果一个函数的实参值个数不确定,就必须用arguments代替形参列表!

//定义付款函数,支持手机支付,现金支付,刷卡支付
function fn(){
  //如果没有传入实参值
  if(arguments.length==0){
    console.log("足球")
  }else if(arguments.length==1){//否则如果只传入一个实参值
    console.log(`1`)
  }else{//否则
    console.log(`刷卡支付,从您卡号${arguments[0]}中扣款成功!`)
  }
}
//想手机支付: 
fn();
//想现金支付:
fn(100);
//想刷卡支付:
fn("6553 1234","123456");

原生函数中重载的例子: arr.splice()

  • 删除元素: arr.splice(i, 几个)
  • 插入新元素: arr.splice(i, 0, 新值1, 新值2,…)
  • 替换现有元素: arr.splice(i, 几个, 新值1, 新值2,…)

07 函数提升与变量提升原型链综合面试题

    function Foo() {
      getName = function() {
        console.log(1)
      }
      return this
    }
    Foo.getName = function() {
      console.log(2)
    }
    Foo.prototype.getName = function() {
      console.log(3)
    }
    function getName() {
      console.log(5)
    }
    var getName = function() {
      console.log(4)
    }
    
    Foo.getName() // 2
    getName() // 4 // JavaScript中函数是一等公民, 函数提升比变量提升的优先级高
    console.log(Foo())
    Foo().getName() // 1 Foo()返回 this 指向 window, 就相当于 this.getName() => window.getName()
    getName() // 1
    new Foo.getName() // 2
    new Foo().getName() // 3
    new new Foo().getName() // 3
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值