js知识点总结一

77 阅读7分钟

作用域

什么是js的作用域

js的执行环境中变量和函数能访问的范围,叫做作用域。作用域定义了函数或变量有权访问的其他数据。作用域都有一个变量对象。

作用域的类型

  • 全局作用域

    1. 页面打开创建,页面关闭销毁
    2. 声明在javascript标签中的变量,可以在任意的地方访问到
    3. 全局作用域中生命的变量或者函数可以在window上被访问到,所有的全局变量都是window的属性
  • 函数作用域

    1. 函数调用的时候被创建,函数执行结束之后,被销毁
    2. 函数的作用域可以向上层作用域访问变量,但是同层的函数作用域不能相互访问
    3. 函数作用域,在每次函数被调用的时候,都会创建一个新的作用域,相互之间没有影响
  • 块级作用域

块级是es6之后才有的,在声名let/const变量的时候会被创建

作用域链

  • 作用域链能保证执行环境对其可访问的变量和函数的有序访问。
  • 在创建函数的时候,会创建一个预先包含全局变量对象的作用域链,这个作用域链被放在函数的Scope里,在调用函数的时候,就会创建一个函数的执行环境,并且会复制函数的Scope中的对象构建一个执行环境作用域链,创建活动对象AO,并把其推入执行环境的作用域链,在函数执行完毕之后,作用域链就会被销毁。

全局预编译和函数预编译

变量对象属性的值最开始都是undefiend

全局预编译

  1. 全局上下文,创建变量对象VO
  2. 先把变量声明作为VO的属性 值为undefined
  3. 再把函数声明作为VO的属性 值为函数本身
  4. 当函数声明的名称和变量声明的名称冲突时,用函数替换变量
console.log(a)	// 函数a
var a = 100;
function a () { }
a = 100
console.log(a) // 100

/**
 * vo = {
   a: undefined funcA
 }
 * **/

VO的创建之后,先找a: undefiend,之后遇到funcA替换。VO声明结束,开始执行代码。

函数预编译

  1. 函数上下文创建之后,会创建变量对象AO
  2. 先找变量作为AO的属性 undefined
  3. 设置形参为AO的属性 undefined
  4. 实参给形参赋值,替换AO中形参的属性
  5. 设置函数声明为AO属性,值为函数本身
  6. 当函数声明的名称和变量声明的名称冲突时,用函数替换变量

内存与数据

内存

  • js程序运行在内存里
  • js变量的声明和函数的声明,调用都会占用内存
  • 内存分为栈内存和堆内存

数据类型

  • 基本数据类型
    1. number,string,null, undefined,boolen,biginit,symbol
    2. 值不能改变。在下面的代码中,a的值不是直接在栈内存里找到a,然后直接改变。而且开辟一个新的内存空间,把值变成a+1的结果,然后复制给a。之前就的a = 1的栈内存被变成了可以回收的状态,在之后会被回收掉。
    3. 数据存放在栈里
let a = 1;
a = a+1
  • 引用数据类型
    1. object,arrary,Date,RegExp,function
    2. 值可以被改变。
    3. 数据存放在堆里,堆的地址存放在栈里。访问引用数据,是先找到地址,然后从地址里面得到真正的数据。
let obj1 = {
  a: 1
}
let obj2 = obj1
console.log(obj1 === obj2)// true
obj2.a = 2
console.log(obj1.a) //  2

深拷贝/浅拷贝

浅拷贝

拷贝的属性如果是引用类型,会被共享

  • Object.create()
  • Object.assign()
  • 扩展运算符...

深拷贝

拷贝的属性如果是引用类型,不会被共享

  • JSON.parse(JSON.stringify(xx))

这个方法在处理对象属性是function,Symbol,undefined属性会丢失,属性是正则的话对应的数据会丢失

  • 自定义deepClone函数处理
function deepClone(obj, map = new WeakMap()) {
  if (obj === null || typeof obj === 'function' || obj instanceof Date || obj instanceof RegExp || typeof obj !== 'object') {
    return obj
  }
  if (map.get(obj)) {
    return map.get(obj)
  }
  if (Array.isArray(obj)) {
    const ressult = []
    map.set(obj, result)
    obj.forEach((item) => {
      ressult.push(deepClone(item, map))
    })
    return ressult;
  }
  
  if (obj instanceof Map) {
    const result = new Map()
    map.set(obj, result)
    for(let val of obj) {
      result.set(val[0], deepClone(val[1], map))
    }
    return result;
  }
  if (obj instanceof Set) {
    const result = new Set()
    map.set(obj, result)
    for(let val of obj) {
      result.add(deepClone(val, map))
    }
    return result;
  }
  // 放在最后防止,arrary,map,set类型进入
  if (obj instanceof Object) {
    const result = {}
    map.set(obj, result)
    for(let key in obj) {
      result[key] = deepClone(obj[key], map)
    }
    return result;
  }
}

闭包

能访问其他函数作用域里的对象的函数,称为闭包。

  • 函数嵌套
function foo() {
  var a = 1
  return function bar() {
    console.log(a)
  }
}

var fn = foo()
fn()
  • 回调函数
function foo(cb) {
  var a = 1;
  cb(a)
}

function bar(a) {
  console.log(a)
}

foo(bar);

this

this的基本概念

  • this不能在代码执行期间被赋值。
  • this会随着执行环境而改变。
var bar = 2;
var obj = {
    bar: 1,
    foo: function() {
        console.log(this.bar)
    }
}
obj.foo() // 1
var foo = obj.foo
foo() // 2

this的指向

  • 基本遵循指向调用者
  • new改变this指向,指向为new生成的实例
  • apply,call 改变this指向,指向为第一个参数对象,如果没有传递,就是指向window

事件循环(event loop)

js是一个单线程执行的,在代码执行的时候,由执行栈来执行任务,执行过程中,异步任务会被放入事件循环里,异步任务由分宏任务和微任务。在事件循环里面会执行当前的宏任务(从宏任务队列中取出一个任务推入执行栈),宏任务产生的微任务放入微任务队列,宏任务执行完毕之后,会处理微任务队列里所有的微任务。

  • 宏任务:setTimeout, setInterval,ajax,dom事件,顶层代码
  • 微任务:promise
  • 执行栈: 执行栈是一个存储函数调用的栈结构,用于跟踪代码的执行顺序。当函数被调用时,它会被推入执行栈,并在执行完毕后被弹出。JavaScript 是单线程的,意味着同一时间只能执行一个任务,执行栈就是用来管理这些任务的执行顺序。
function foo() {
  console.log("foo");
}

function bar() {
  console.log("bar");
  foo();
}

bar();

在这个例子中,执行栈会按照以下顺序进行操作:

  1. bar 函数被调用,推入执行栈。
  2. console.log("bar") 执行完毕,bar 函数从执行栈弹出。
  3. foo 函数被调用,推入执行栈。
  4. console.log("foo") 执行完毕,foo 函数从执行栈弹出。

ajax的实现原理

// 1. 创建ajax请求对象
const xhr = new XMLHttpRequest()
// 传递请求方式和url
const url = 'xxx';
xhr.open('get', url)
// 发送请求
xhr.send()
// 接收数据
xhr.onload = () => {
  // todo
}

同源策略

同源必须是同协议,同域名,同端口

跨源访问的方法

  • script标签嵌入跨域脚本
  • link引入css
  • image图片
  • video/audio播放多媒体资源
  • 通过object,embed,applet嵌入的插件
  • @font-face引入的字体插件
  • iframe载入资源

解决跨域的方法

  • cors
  • jsonp
 function jsonp(options) {
    // 创建script
    const script = document.createElement('script')
    script.src = options.url
    // body中插入script
    document.body.appendChild(script)
    // script加载资源之后删除script
    script.onload = () => {
      document.body.removeChild(script)
    }
  }
  jsonp({
    url: 'http://127.0.0.1:5501/jsonp/'
  })
	// 这里声明fn是因为服务端会返回一个'fn()',fn就可以自动被调用了
  function fn() {
    console.log('调用')
  }

简单请求/非简单请求

  • 简单请求
    • 请求方式为get head post
    • 请求中没有自定义的头部
    • content-type: (application/x-www-form-urlencoded、multipart/form-data、text/plain)只能是这3个中任意一个
  • 非简单请求
    • 请求方式由put, delete, post
    • 请求中有自定义的头部
    • content-type不是text/plain, multipart/form-data, application/x-www-form-urlencoded中的任意一个。

预检请求是确保服务器是否允许发送实际的请求。

面向对象

面向对象是一种编程方式,把代码封装起来,便于维护和扩展。

具有封装,继承,多态的三大特性。

封装

工厂模式

 function car(color, name) {
  let obj = new Object()
  obj.name = name;
  obj.color = color
  return obj
}
let car1 = car('红', '宝马')
let car2 = car('蓝', '奔驰')

构造函数

function Car(color, name) {
  this.name = name
  this.color = color
}

let car1 = new Car('红', '宝马')
let car2 = new Car('蓝', '奔驰')

原型和原型链

在js里每个对象都有一个被称为原型的隐藏属性,原型里面包含了共享的属性和方法,可以被其他的对象继承。

对象都有一个隐式的链接指向它的原型,这个链接称为原型链。

  • 实例对象通过__proto__指向原型对象
  • 构造函数通过prototype指向原型对象
  • 实例对象的constructor指向构造函数
console.log(obj.__proto__ === Fn.prototype) // true
console.log(obj.constructor === Fn) // true
console.log(Fn.prototype.constructor === Fn) // true
console.log(Fn.prototype === obj) // false
console.log(Fn.__proto__ === Function.prototype) // true
console.log(Object.__proto__ === Function.prototype) // true
console.log(Fn.__proto__ === Object.__proto__) // true
console.log(Fn.prototype.__proto__ === Object.prototype) // true
console.log(Fn.prototype.__proto__.__proto__) // null