【JavaScript】知识点

88 阅读3分钟

变量类型和计算

typeof能判断哪些类型

  • 能识别六种值类型(undefined, string, number, boolean, symbol, bigint)
  • 能判断 function 类型
  • 不能判断三种引用类型(object, array, null)

值类型和引用类型的区别

  • 值类型直接存在栈
  • 引用类型存放在堆,地址存在栈

在物理结构上,堆栈存放在一块区域,栈从前往后放置,堆从后往前放置

【……栈栈->……………………<-堆堆……】

计算问题

  • 加法运算
const a = 100 + 10    // 110
const b = 100 + '10'  // '10010'
const c = true + '10' // 'true10'
  • == 运算
100 == '100'       // true
0 == ''            // true
0 == false         // true
false == ''        // true
null == undefined  // true

/**
  除了 == null 之外,其他一律用 ===
  因为: obj.a == null,
  相当于:obj.a === null || obj.a === undefined
*/
  • 逻辑运算
// truly和falsely
!!0 === false
!!NaN === false
!!'' === false
!!null === false
!!undefined === false
!!false === false
// 其余全为true
// 在if(exp)内部的表达式就是truly和falsely

手写深拷贝

function deepClone(obj) {
  if(typeof obj !== 'object' || obj == null){
    return obj;
  }

  let res;
  if(obj instanceof Array){
    res = [];
  }else{
    res = {}; 
  }

  for(let key in obj){
    if(obj.hasOwnProperty(key)){
      res[key] = deepClone(obj[key]);
    }
  }
  return res;
}

原型和原型链

instanceof

  • xxx instanceof Array
  • xxx 这个值或者对象 是否继承自 Array
  • Array 是否存在于 xxx 的原型链上

继承

class People{
  constructor(name){
    this.name = name;
  }
}

class Student extends People{
  constructor(name, number){
    super(name);
    this.number = number;
  }
}

原型 & 原型链

原型

  • 每个class都有显式原型prototype
  • 每个实例对象都有隐式原型__proto__
  • 实例对象的__proto__指向 对应 class 的 prototype

原型链

手写一个jQuery,考虑插件和扩展性

class jQuery{
  constructor(selector){
    const result = document.querySelectorAll(selector);
    const length = result.length;
    for(let i = 0; i < length; i++){
      this[i] = result[i];  
    }
    this.length = length;
    this.selector = selector;
  }
  get(index){
    return this[index];
  }
  each(fn){
    for(let i = 0; i < this.length; i++){
      const elem = this[i];
      fn(elem);
    }
  }
  on(type, fn){
    return this.each(elem => {
      elem.addEventListener(type,fn,false);
    });
  }
  // 可以扩展很多DOM API
}

// 添加插件
jQuery.prototype.dialog = function(info){
  alert(info);
}

// 复写,也就是'造轮子' 
class myJQuery extends jQuery{
  constructor(selector){
    super(selector);
  }
  // 扩展自己的方法
}

// 使用
const $p = new jQuery('p');
$p.get(1);
$p.each((elem) => console.log(elem.nodeName));
$p.on('click', () => alert('clicked'))

作用域和闭包

作用域 & 自由变量

作用域

  • 全局作用域
  • 函数作用域
  • 块级作用域(ES6新增)

自由变量

  1. 一个变量在当前作用域没有定义,但被使用了
  2. 向上级作用域,一层一层依次寻找,知道找到为止
  3. 如果到全局作用域都没找到,则报错 xx id not defined

闭包及应用场景

闭包是作用域应用的特殊情况,有两种表现:

  1. 函数作为参数被传递
  2. 函数作为返回值被返回

自由变量的查找规则:

所有的自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方!!!

function print(fn){
  let a = 200;
  fn();
}
let a = 100;
function fn(){
  console.log(a);
}
print(fn); // 100

应用场景:

  • 用闭包隐藏数据,只暴露API
function createCache() {
  const data = {};
  return {
    set: function(key, val) {
      data[key] = val;
    },
    get: function(key) {
      return data[key]
    }
  }
}
const c = createCache();
c.set('a',100);
console.log(c.get('a'));

this在不同应用场景下,如何取值

  • 作为普通函数
  • 使用call,apply,bind
  • 作为对象方法被调用
  • 在class方法中调用
  • 箭头函数
  • 原型链上的this
原型链的this:xialuo.__proto__.sayHi.call(xialuo)

this 取的值是在执行的时候确定的,不是在定义的时候确定的。

手写bind函数

Function.prototype.myBind = function () {
  const args = Array.prototype.slice.call(arguments);
  const t = args.shift();
  const self = this;
  return function(){
    return self.apply(t,args);
  }
}

异步

JS是单线程的,只能同时做一件事

浏览器和nodejs已支持JS启动进程,如 Web Worker

JS和DOM渲染共用一个线程,因为JS可修改DOM结构

遇到等待(发送请求,定时任务)不能卡住,所以需要异步,通过回调实现

同步和异步的区别是什么

  • 基于JS是单线程语言
  • 异步不会阻塞代码执行
  • 同步会阻塞代码执行

应用场景

  • 网络请求,如 ajax 图片加载
  • 定时任务,如 setTimeout

Promise 实现异步图片加载

function loadImg(src) {
  const p = new Promise(
    (resolve, reject) => {
      let img = document.createElement('img');
      img.onload = () => {
        resolve(img);
      }
      img.onerror = () =>{
        reject(new Error(`图片加载失败 ${src}`))
      }
      img.src = src;
    }
  )
  return p;
}

const url1 = '/xxx.png';
const url2 = '/yyy.png';

loadImg(url1).then(img1 => {
  console.log(img1.width)
  return loadImg(url2);
}).then(img2 =>{
  console.log(img2.width);
}).catch(err => console.log(err))