Javascript 的数据类型

  • Javascript 核心分为两大数据类型

    • 基本数据类型

      • Number

      • String

      • Boolean

      • Undefined

      • null

      • Symbol

    • 引用数据类型

      • Object

      • Array

      • Map

      • Set

      • Function

Javascript Number 数据类型

  • 核心分类就是三种吧:整数、浮点数、NAN

console.log(0/0)
console.log(-0/+0)

Javasscript undefined

  • undefined 类型只有一个值,就是 undefined ,当我们使用 var 或者 let 的时候,进行变量的初始化等操作的时候,默认就是 undefined 值

let msg;
console.log(msg === undefined);  // true
  • undefined 和 为声明变量的区别

    • undefined 的变量是在内存中存在了的,可以被安全的访问,但是对于没有被声明的变量而言,就是不可被安全访问的讷

Javascript String

  • 定义的时候就是通过我们的 "" '' `` 来进行定义吧

  • 字符串本身是不可变的,也就是说字符串被定义了就不可被修改了

Javascript Null

  • Null 类型只有一个值,为 null

  • 逻辑上来说的话,null 表示的是一个空指针,这也是为什么 typeof null === "object" 的原因之一吧

  • undefined 的值是由我们的 null 派生来的讷,null === undefined

Javascript 数据结构

什么是数据结构

  • 数据结构是计算机存储和组织数据的一种方式吧

  • 数据结构意味着接口或者封装,一个数据结构可以被看做是两盒函数之间的接口,或者是由数据类型联合起来的组成的存储内容的访问方式的封装吧

  • 常见的数据结构含有

    • 数组(Array)

    • 栈(Stack)

    • 队列(Queue)

    • 字典(Dict)

    • 散列表(Hash Table)

    • 树(Tree)

    • 图(Graph)

    • 堆(Heap)

数组(Array)

  • 数组是最基本的数据结构,很多语言都内置的一种数据类型

  • 数组是一块连续的内存空间保存数据,保存的数据的个数在分配内存的时候都已经确认了

栈(Stack)

  • 栈数据结构保存的是先进后出的特性(FILO)的有序集合

  • 在栈中新元素接近的是栈顶,旧元素接近的是栈底

  • 每次加入的新元素和拿走元素都在顶部的讷

队列(Queue)

  • 队列是遵循的是先进先出的特性吧(FIFO)

  • 队列的尾部添加新元素,顶部进行移除元素

链表(Linked List)

  • 链表是一种列表

  • 链表几乎可以用在任何可以使用一维数组的情况下吧

  • 链表对于删除元素和添加元素是十分高效的讷

字典(Dict)

  • 字典是一种使用键值对存储数据的数据结构

  • Javascript Object 就是使用键值对的原理进行存储的我们的数据结构吧

散列表(Hash Table)

  • 也是哈希表吧,特点是散列表上插入、删除和取出数据是十分的块的讷

Javascript DOM 操作

  • 1. DOM 操作

  • 2. 基于 DOM 操作实现创建节点

  • 3. 基于 DOM 操作实现查询节点

  • 4. 基于 DOM 操作实现更新节点

  • 5. 基于 DOM 操作实现添加节点

  • 6. 基于 DOM 操作实现删除节点

DOM

  • DOM 就是文档对象模型讷(Document Object Model)文档的编程接口吧

  • 他实现提供了对于文档的结构化的描述,并且实现定义了一种方式从程序中堆结构进行访问,从而实现改变文档的结构、样式或者内容

<div>
  <p>
    hello world
  </p>
</div>
  • 上面的数据结构抽象来说的话就是基于我们的:div --> p --> content 的一种的文档指向顺序吧hello world 就是我们的属性节点吧

DOM 操作

  • 以前进行前端开发的时候,我们常用的是库是 jquery zepto 等 javascript 库来实现快捷的操作浏览器的真实 DOM

  • 后续出现了当下的虚拟 DOM 的开发框架类似于 vue react angular 等框架,进行虚拟 DOM 的抽象原本的 DOM 结构,通过数据驱动的形式进行操作我们的文档对象模型,但是原生的 DOM 操作在实际的很多开发中还是非常常用的讷,对于理解底层的有些设计很有帮助讷

  • 常见的 DOM 操作有

    • 1. 创建节点

    • 2. 查询节点

    • 3. 更新节点

    • 4. 添加节点

    • 5. 删除节点

创建节点

  • createElement

    • 创建新元素,接受的是一个参数,也就是需要被创建的标签名吧

const divEl = document.createElement("div")
  • createTextNode

    • 创建一个文本节点吧

const textNode = document.createTextNode("content")
  • createDocumentFragment

    • 用于实现的是创建文档碎片出来,表示的是一个轻量的文档,主要是用于存储临时的节点,然后把文档碎皮那的内容一次性插入到 DOM 中

    • 当我们把该 fragment 插入到文档模型中的时候,不是插入的文档中,而是其子节点插入其中讷,也就是其实际的内容插入进去讷

const fragment = document.createDocumentFragment()
  • createAttribute

    • 创建属性节点,可以实现自定义属性的讷

const attr = document.createAttribute("suctom")

获取节点

  • querySelector

  • querySelectorAll

更新节点

  • innerHTML

  • innerText、textContext

  • style

添加节点

  • innerHTML

  • appendChild

  • insertBefore

  • setAttribute

删除节点

Javascript BOM 操作

  • BOM 是 browser object model 浏览器对象模型讷!提供了独立于内容和浏览器窗口进行交互的操作对象把

  • 其核心作用就是进行和浏览器做一些交互实现把,比如进行页面的后退、前进、刷新、浏览器窗口发生变化的操作、滚动条的滚动、以及获取用户信息等

  • 对于浏览器的全部内容可以被看做是一个 DOM 对象,整个浏览器可以被看做是一个 BOM

window 对象

  • BOM 的核心对象就是 window 对象吧,也就是一个浏览器的实例吧

  • 在浏览器中 window 对象有两层角色扮演,一个是浏览器窗口的接口、也是一个全局对象讷

  • 所以说基本上所有的api都是在这个 window 中的讷

  • 如果对于窗口的控制来说的话我们可以做的是

    • moveBy(x, y) 可以控制窗口的移动

    • moveTo(x, y) 控制窗口的移动吧

    • resizeBy(x, y)

    • resizeTo()

    • `srollTo()`

    • scrollBy()

location 对象

  • 核心是进行对应的获取到 url 的一些地址信息的吧

  • hash host hostname herf pathname port protocol search

    • 哈希值的获取,存在 #

    • 服务器名称 + 端口号

    • 域名

    • url 的路由信息吧

    • 端口号

    • 协议类型

    • 查询参数,存在 ? 吧

screen 对象

history 对象

typeof 和 instanceof 需要进行明确区分一下吧

Javascript 原型和原型链

原型

  • Javascript 是一个基于原型的语言吧,每个对象拥有一个原型对象吧

  • 当我们尝试着去访问对象上的属性时,它不仅仅在该对象上进行寻找,也会到该对象的原型上进行寻找讷,以及该对象的原型的原型,依次层层向上搜索实现讷,直到找到一个名字匹配的属性或者达到原型链的末端吧

  • 函数是具备属性的讷,每个函数都是具备一个特殊的属性叫做原型 prototype 吧

function Foo() {}

console.log(Foo.prototype)

Foo的原型property --> Foo.property --> 其 constructor 指向 Foo 

原型链

  • 原型对象也是拥有属于自己的原型的讷,并从中集成属性和方法讷,prototype chain

  • 在对象实例和构造器之间简历一个连接 proto 属性,从构造函数的 prototype ,之后回溯到原型链吧,在构造器中找到这些属性和方法

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

    this.sayName = function() {
        console.log(this.name)
    }

    this.sayAge = function() {
        console.log(this.age)
    }
}

// 开始初始化
const p1 = new Person('uuser', 20)
p1.sayName()
p1.sayAge()

  • 构造函数 Person 存在原型对象为 Person.prototype

  • 构造函数生成实例对象 person ,person 的 __proto__ 指向构造函数的原型对象

Javascript 作用域链

作用域

  • 作用域:也就是我们的变量,也是作用域上下文和函数生效的区域集合

  • 作用决定了代码区块中的变量和其他资源的可见性吧

  • 作用域的分类

    • 全局作用域

    • 局部作用域

    • 块级作用域

全局作用域

  • 任何不在函数中或是大括号中声明的变量,都是在全局作用域下的讷,全局作用域下声明的变量可以在程序中的任意位置进行访问讷

函数作用域

  • 函数作用域也是我们的局部作用域,如果一个变量在函数内部声明的,他就是在一个函数作用域里面的讷!这些变量只能在函数内部被访问讷,不能再函数以外被访问

块级作用域

  • 词法作用域,也是静态作用域,变量被创建的时候就确定好了的,而非执行阶段确定的讷,也是说我们写好代码时,他的作用域就被确定了吧,javascript 遵循的就是词法作用域

Javascript this

this 定义

  • 函数的 this 关键字再 javascript 中表现略有异同,此外,在严格模式和非严格模式下的也是存在差异的讷

  • 在绝大多数的情况下,函数的调用方式也是决定了 this 的值吧(运行时绑定吧)

  • this 关键字是函数运行时自动生成的一个内部对象吧,只有函数在内部使用的时候,总指向调用他的对象讷

  • 在函数调用的时候,this 被绑定了,就无法被修改了吧

function foo() {
    console.log(this)
}

function bar() {
    foo.call(this)
}

const obj = {
    name: 'obj',
}

foo()
bar.call(obj)
$ node index.js 
<ref *1> Object [global] {
  global: [Circular *1],
  clearImmediate: [Function: clearImmediate],
  setImmediate: [Function: setImmediate] {
    Symbol(nodejs.util.promisify.custom): [Getter]
  },
  clearInterval: [Function: clearInterval],
  clearTimeout: [Function: clearTimeout],
  setInterval: [Function: setInterval],
  setTimeout: [Function: setTimeout] {
    Symbol(nodejs.util.promisify.custom): [Getter]
  },
  queueMicrotask: [Function: queueMicrotask],
  structuredClone: [Function: structuredClone],
  atob: [Function: atob],
  btoa: [Function: btoa],
  performance: [Getter/Setter],
  fetch: [Function: fetch],
  crypto: [Getter],
  navigator: [Getter]
}
{ name: 'obj' }

this 绑定规则

  • 默认绑定

    • 全局环境中定义的一个函数,内部使用的 this指向的就是 window 吧

  • 隐式绑定

  • new构造绑定

  • 显示绑定

Javascript new 操作符

  • new 操作符实现的是创建一个给定构造函数的实例对象吧

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

Person.prototype.sayName = function() {
    console.log(this.name)
}

const person = new Person('obj', 18)
person.sayName()
  • new 通过构造函数 Person 创建出对应的实例访问到构造函数中的属性讷

  • new 通过构造函数 Person 创建出来的实例可以访问到构造函数的 prototype chain 上的属性和方法吧(实例和构造函数通过原型链连接了起来吧)

流程是

  • 创建出一个新的 obj 吧,后续会被返回的讷

  • 将新创建出来对象和构造函数的原型链进行绑定起来讷

  • 将构造函数的 this 绑定到新的 obj 上吧

  • 根据构建函数返回类型做判断,如果是原始值直接忽略,如果是对象即可直接返回即可吧

function myNew(Func, ...args) {
    // 1. 创建出空对象吧
    const obj = {};

    // 2. 绑定原型链
    obj.__proto__ = Func.prototype;

    // 3. 修改 obj 的 this 吧
    const res = Func.apply(obj, args);

    // 4. 最后进行对应的返回实例吧
    return res instanceof Object ? res : obj;
}

Javascript bind call apply

作用

  • 都是进行对应的显式的绑定 this 的讷,改变函数执行的时候的上下文的讷,换言之就是改变函数运行时JIT的时候的 this 指向的内置api吧

区别

  • apply 实现的是接受两个参数,一个是需要运行时绑定修改的 this 指向,一个是函数执行的时候需要的函数参数吧,使用的是数组的形式传递参数吧

    • 内部执行的是流程是

      • 1. 先进行上下文和参数的校验和兜底的修改吧

      • 2. 修改上下文的this 吧

      • 3. 执行函数获取结果

      • 4. 实现将结果进行返回即可

    • 内部顺序是

      • 第一步进行的参数的校验工作吧

      • 第二部进行的是获取得到执行函数以及设置其上下文,也就是进行绑定 this 讷

      • 第三步就是进行调用函数获取得到返回值结果

      • 第四步就是删除创建出来的多余的 key 吧

      • 最后就是将返回值进行返回即可吧

function Person() {
    console.log(this.name, this.age)
}

Person.apply({ name: "juwenzhang", age: 18 }, [])  // juwenzhang 18
Function.prototype.myApply = function(context, args) {
    const ctx = context || globalThis;
    const arg = Array.isArray(args) ? args : [];
    const fnKey = Symbol('fn');

    // 将函数的 key 设置给我们的上下文以及进行执行函数为调用的 this 吧
    ctx[fnKey] = this;
    let res;
    try {
        res = ctx[fnKey](...arg);
    } catch (e) {
        throw e;
    } finally {
        delete ctx[fnKey];
    }
    return res;
}
  • call 和 apply 几乎是一样的讷

Function.prototype.myCall = function(context, ...args) {
    const ctx = context || globalThis;
    const fnKey = Symbol('fn');

    ctx[fnKey] = this;

    let res;
    try {
        res = ctx[fnKey](...args);
    } catch (e) {
        throw e;
    } finally {
        delete ctx[fnKey];
    }
    return res;
}

在 apply 和 call 中实现运行时修改 this 的策略方案就是在函数执行的是时候将函数的动态的放入到自己需要绑定的上下文中,这个就是底层的绑定 this 的原理吧,还是非常简单的讷