linding

目录

一、浏览器工作原理和V8引擎

1、浏览器内核和js引擎的关系
* WebCore:负责HTML解析、布局、渲染等等相关的工作
* JavaScriptCore:解析、执行JavaScript代码
2、V8引擎的原理
* JavaScript源代码
    - 词法分析:解析源代码中每个单词的类型、值等信息
    - 语法分析:根据单词的类型信息可做语法分析,生成抽象语法树
* AST抽象语法树(格式固定的树结构对象。babel原理:ts -> ast -> js)
* MachineCode优化的机器码(多次执行的字节码函数会被标记为hot函数,hot函数会优化为固定的一组机器指令,
  无需每次执行都将字节码函数转成机器指令,从而提高执行效率。hot函数优化生成的机器指令,会由于类型等原因
  造成机器指令执行不正确,机器指令则会反向生成字节码函数,再由字节码函数转成机器指令执行,类型确定是ts执
  行效率高于js的原因。)
* bytecode字节码

二、JavaScript的执行过程

1、全局代码执行和作用域提升
* 执行上下文栈(ECStack)
      全局执行上下文(GEC)
          变量对象(VO) == 全局对象(GO)
      函数执行上下文(FEC)
          变量对象(VO) == 激活对象(AO)
          作用域链 == AO + 父级作用域 + GO
* 声明提升:还未进行变量赋值和函数执行的过程。声明变量:默认是undefined。声明函数:会在堆内存开辟空间
  以存放函数代码块,再将内存地址赋给声明的函数。
* 延迟解析:将全局作用域下执行的函数进行预解析,非全局作用域下执行的函数(嵌套函数),在函数被调用时才会
  全量解析,提高网页的运行效率。
* JavaScript 函数作用域: 作用域在函数内修改。
* var a = b = 10 等价 var a = 10; b = 10(未用var声明的变量,会成为GO的属性)。
* var a, b = 10 等价 var a; var b = 10

三、JS的内存管理和闭包

1、常见的GC算法
* 引用计数
* 标记清除
2、闭包
/**
 * 高阶函数:当function的参数或者返回值也为函数时,则称这个function为高阶函数
 * 闭包:函数+可以访问的自由变量
 * 基本数据类型所占内存空间:小于2**32的整数占4字节32位,小数占8字节64位
 */
function foo() {
    var name = "foo"
    // 内层函数(bar代码块)未使用到的自由变量,js引擎会做优化将其销毁
    var age = 18

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

    return bar
}

// 内层函数(bar代码块)被fn引用着,GC不处理则不释放内层函数(bar代码块)所占的内存空间
var fn = foo()
fn()

// 释放内层函数(bar代码块)所占的内存空间
fn = null
// 释放外层函数(foo代码块)所占的内存空间
foo = null

四、关于javascript中的this

1、this在全局作用域下
* 浏览器:this指向window
* Node环境:this指向空对象({})。Node环境会将.js文件作为一个module,放入一个函数中,
  并使用.call({})方式执行这个函数。
2、this的四种绑定规则
* 默认绑定(fun()):独立函数调用this指向window
* 隐式绑定(object.fun()):object对象会被js引擎绑定到fun函数中的this里面
* 显示绑定:
      - apply:fun.apply(object,[itemX]);
      - call:fun.call(object,itemX);
      - bind:var funBind = fun.bind(object); funBind();
* new绑定:function Person(name) { this.name = name; }
  this指向函数作为构造器new出来的对象
* 优先级:new绑定 > 显示绑定(bind > call/apply) > 隐式绑定 > 默认绑定
3、this的其他补充(非箭头函数)
* setTimeout(fun):函数中this指向window
* div.onclick=fun:函数中this指向div
  div.addEventListener("click",fun):函数中this指向div。
* 数组forEach/map/filter/find:有参数2则函数中的this指向参数2,没有则this指向window
* 忽略显示绑定:apply/call/bind参数传null/undefined相当于独立函数调用this指向window
* 间接函数引用:(obj1.fun=obj2.fun)() 是独立函数调用this指向window
* (object.fun)() 等价 object.fun() 是隐式绑定this指向object
4、箭头函数
* ()=>({}):只有一行代码并且返回一个对象的简写方式
* 箭头函数的this获取:箭头函数不绑定this,this从上层作用域获取。call/apply/bind绑定无效
5、call函数的实现
// 参数1:绑定的对象;参数2:剩余参数方式接收参数,args为数组
Function.prototype.mycall = function (bind, ...args) {
    // 当xyz.mycall()则this指向xyz
    var fun = this
    // bind传入基本数据类型则要转成包装类型对象,传入null/undefined则为window对象
    var rebind = (bind !== null && bind !== undefined) ? Object(bind) : window
    rebind.fun = fun
    // 展开运算符方式传入参数
    var res = rebind.fun(...args)
    delete rebind.fun
    return res
}

function xyz(a, b) {
    console.log(this)
    return a + b
}

console.log(xyz.mycall({}, 1, 2))
6、类数组对象arguments
function fun(...args) {
    // 知识点一、类数组对象:有length属性,可通过数组下标获取元素
    console.log(arguments.length)
    console.log(arguments[0])
    // 指向函数体本身,严格模式("use strict";)下会报错
    // console.log(arguments.callee)

    // 知识点二、类数组对象转数组
    // 方式1:
    var arr1 = []
    for (var i = 0; i < arguments.length; i++) {
        arr1.push(arguments[i])
    }
    console.log(arr1)
    // 方式2:
    var arr2 = Array.prototype.slice.call(arguments)
    console.log(arr2)
    // 方式3:
    var arr3 = [].slice.call(arguments)
    console.log(arr3)
    // 方式4:
    var arr4 = Array.from(arguments)
    console.log(arr4)
    // 方式5:
    var arr5 = [...arguments]
    console.log(arr5)

    // 知识点三、箭头函数不绑定arguments,从上层作用域获取,全局作用域没有arguments
    var xyz = () => {
        console.log(arguments)
    }
    xyz()
}

fun(1, 2, 3)

五、函数式编程

1、纯函数
* 相同的输入一定产生相同的输出
* 在执行的过程中不会产生任何的副作用
2、柯里化
* 柯里化的好处:对上层函数逻辑的复用
* 简化写法:var fun = x => y => z => x + y + z
3、通用柯里化函数的实现
function autoCurrying(fun) {
    return function curried(...args1) {
        // 函数的length属性表示可传参数的长度
        if (args1.length >= fun.length) {
            return fun.apply(this, args1)
        } else {
            return function (...args2) {
                return curried.apply(this, args1.concat(args2))
            }
        }
    }
}

function fun(time, level, message) {
    console.log(`[${time.getHours()}:${time.getMinutes()}][${level}][${message}]`)
}

var curryingFun = autoCurrying(fun)
curryingFun(new Date())("DEBUG")("这是一段中文")
curryingFun(new Date(), "DEBUG")("这是一段中文")
curryingFun(new Date(), "DEBUG", "这是一段中文")
4、组合函数
* 组合函数:依次执行的多个函数组合在一起
5、通用组合函数的实现
function composeFun(...funs) {
    var length = funs.length;
    for (var i = 0; i < length; i++) {
        if (typeof funs[i] !== "function") {
            throw new TypeError("Expected arguments are functions")
        }
    }
    return function (...args) {
        var index = 0
        var result = length ? funs[index].apply(this, args) : args
        while (++index < length) {
            result = funs[index].call(this, result)
        }
        return result
    }
}

function fun1(a, b) {
    return a + b
}

function fun2(c) {
    return c * 2
}

var fun = composeFun(fun1, fun2)
console.log(fun(1, 2))

六、with-eval-strict

1、with
var obj = {message: "obj"}

function fun() {
    var message = "fun"
    /**
     * with语句:可以形成自己的作用域
     * 作用域链:传入的对象 -> 父级作用域 -> GO
     * 严格模式下会报错,不推荐使用
     */
    with (obj) {
        console.log(message)
    }
}

fun()
2、eval
* eval可以将传入的字符串当做javascript代码来运行
* 不推荐使用:代码可读性差;字符串易被篡改,有被攻击的风险;js引擎无法优化
3、严格模式
* 开启严格模式:
      - 作用整个文件:文件第一行加"use strict";
      - 作用某个函数:函数第一行加"use strict";
* 严格模式常见的限制
      - 禁止意外创建全局变量
      - 不允许函数有相同的参数名称
      - 静默错误
      - 不允许使用原先的八进制格式 0123
      - with语句不允许使用
      - eval函数不会向上引用变量了
      - 在严格模式下,自执行函数(默认绑定)会指向undefined
      - setTimeout(fun,delay)的this,fun.apply(this = window)

七、js面向对象(字面量)

1、创建对象的方式
* 通过new Object()创建:var obj = new Object();
* 字面量形式:var obj = {}
2、对象属性的操作
var obj = {
    name: "黄婷婷",
    age: 18
}
// 1、获取属性的值
console.log(obj.name)
// 2、设置属性的值
obj.name = "孟美岐"
// 3、删除属性
// delete obj.age
console.log(obj)
// 4、遍历属性
for (const objKey in obj) {
    console.log(objKey)
}
Object.keys(obj).forEach(objKey => {
    console.log(objKey)
})
3、defineProperty方法(数据属性描述符)
var obj = {
    name: "黄婷婷"
}
/**
 * 未使用属性描述符定义的属性("name"属性),也具备对应的特性:
 *     value: 赋值的value
 *     writable: true
 *     configurable: true
 *     enumerable: true
 */
Object.defineProperty(obj, "address", {
    // 默认值undefined
    value: "无锡市",
    // 默认值false。该特性表示"address"属性不可赋值(写入值)
    writable: false,
    // 默认值false。该特性表示"address"属性不可删除(严格模式下会报错),也不可重新定义属性描述符
    configurable: false,
    // 默认值false。该特性表示"address"属性不可枚举
    enumerable: false
})
obj.address = "上海市"
delete obj.address
console.log(Object.keys(obj))
console.log(obj)
4、defineProperty方法(存取属性描述符)
var obj = {
    // 下划线开头的属性常用来表示私有的属性
    _address: ""
    _age: 18,
    // configurable默认true,enumerable默认true
    get age() {
        return this._age
    },
    set age(value) {
        this._age = value
    }
}
// get/set不可与数据属性描述符的value/writable同时使用
/**
 * 作用一、隐藏某一个私有属性,不希望直接被外界使用和赋值
 * 作用二、如果我们希望截获某一个属性它访问和设置值的过程时,也会使用存取属性描述符
 */
Object.defineProperty(obj, "address", {
    configurable: true,
    enumerable: true,
    get: function () {
        return this._address
    },
    set: function (value) {
        this._address = value
    }
})
console.log(obj)
5、定义多个属性描述符
var obj = {
    _age: 18
}
Object.defineProperties(obj, {
    name: {
        value: "黄婷婷",
        writable: true,
        configurable: true,
        enumerable: true
    },
    age: {
        configurable: true,
        enumerable: true,
        get: function () {
            return this._age
        },
        set: function (value) {
            this._age = value
        }
    }
})
6、补充说明Object的方法
var obj = {
    name: "黄婷婷",
    age: 18
}
// 1、获取对象某一个属性的属性描述符
console.log(Object.getOwnPropertyDescriptor(obj, "name"))
// 2、获取对象所有属性的属性描述符
console.log(Object.getOwnPropertyDescriptors(obj))
// 3、禁止对象添加新的属性
Object.preventExtensions(obj)
obj.address = "无锡市"
console.log(obj)
// 4、禁止对象配置/删除里面的属性
Object.seal(obj)
delete obj.name
console.log(obj)
// 5、让属性不可修改(writable:false)
Object.freeze(obj)
obj.name = "孟美岐"
console.log(obj)

八、js面向对象(构造函数)

1、创建对象方案(工厂模式)
// 缺点:获取不到对象最真实的类型
function createPerson(name, age) {
    var person = {}
    person.name = name
    person.age = age
    return person
}

var person = createPerson("黄婷婷", 18)
2、认识构造函数
function Person() {
}

// 通过new关键字去调用一个函数,那么这个函数就是一个构造函数了
var person = new Person(); // 无需传参时小括号可省略
3、创建对象方案(构造函数)
// 规范:构造函数的首字母一般是大写
function Person(name, age) {
    this.name = name
    this.age = age
}

/**
 * 如果一个函数被使用new操作符调用了,那么它会执行如下操作:
 *     - 1、在内存中创建一个新的对象(空对象)
 *         var 空对象 = {}
 *     - 2、构造函数内部的this,会指向创建出来的新对象
 *         this = 空对象
 *     - 3、这个对象内部的[[prototype]]属性会被赋值为该构造函数的prototype属性
 *         this.__proto__ = Person.prototype
 *     - 4、执行函数的内部代码(函数体代码)
 *         Person()
 *     - 5、如果构造函数没有返回非空对象,则返回创建出来的新对象
 */
var person = new Person("黄婷婷", 18);
// 获取对象类型(构造函数名)
console.log(person.__proto__.constructor.name)
4、构造函数缺点
function Person() {
    this.eating = function () {
    }
}

// 缺点:每次new对象eating方法都会创建一个函数,比较浪费内存空间
var person = new Person();
5、对象的原型(隐式原型)的理解
var obj = {}
/**
 * 每个对象中都有一个[[prototype]],这个[[prototype]]可以称之为对象的原型(隐式原型)
 * 1、获取原型对象:
 *     - obj.__proto__
 *     - Object.getPrototypeOf(obj)
 * 2、原型的作用:当我们从一个对象中获取某一个属性时,它会触发[[get]]操作。1、在当前对象中去查
 * 找对应的属性,如果找到就直接使用;2、如果没有找到,那么会沿着它的原型去查找[[prototype]]
 */
console.log(obj)
6、函数的原型(显示原型)的理解
/**
 * 函数也是一个对象(new Function())
 * 函数作为对象来说,它也是有[[prototype]]隐式原型的
 * console.log(Person.__proto__)
 */
function Person() {
}

var p = new Person()
// 函数它因为是一个函数,所以它还会多出来一个显示原型属性:prototype
console.log(p.__proto__ === Person.prototype)
7、构造函数、原型对象、实例对象
原型对象.constructor -> 构造函数
构造函数.prototype -> 原型对象

实例对象.__proto__ -> 原型对象

new 构造函数() -> 实例对象
8、自定义构造函数的原型对象
function Person() {
}

// 1、Person.prototype重新赋值
Person.prototype = {
    name: "黄婷婷"
}
// 2、Person.prototype的constructor属性指向Person构造函数(不可枚举)
Object.defineProperty(Person.prototype, "constructor", {
    value: Person,
    writable: true,
    configurable: true,
    enumerable: false
})
console.log(Person.prototype)
9、创建对象方案(构造函数+原型对象)
function Person(name) {
    // 1、属性定义在构造函数中
    this.name = name
}

// 2、方法定义在原型对象上
Person.prototype.sleep = function () {
    console.log(this.name + " 睡觉")
}

var person = new Person("孟美岐");
person.sleep()

九、js面向对象(三大特性)

1、原型链直接继承的弊端
function Person(name, friends) {
    this.name = name
    this.friends = friends
}

var person = new Person("林志玲", []);

function Student(age) {
    this.age = age
}

Student.prototype = person

// 弊端三:在前面实现类的过程中都没有传递参数
var student = new Student(18);
// 弊端一:打印student对象,继承的属性是看不到的
console.log(student)
// 弊端二:创建两个Student类型对象
// 直接修改对象上的属性,是给本对象添加了一个新属性
student.name = "佟丽娅"
// 获取引用,修改引用中的值,会相互影响
student.friends.push("鞠婧祎")
2、借用构造函数继承的弊端
function Person(name, friends) {
    this.name = name
    this.friends = friends
}

var person = new Person("", []);

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

Student.prototype = person

var student = new Student(18, "林志玲", []);
// 弊端一、Person函数至少被调用了两次
// 弊端二、student的原型对象上会多出一些属性,但是这些属性是没有存在的必要
3、父类原型对象赋值给子类原型对象继承的弊端
function Person(name, friends) {
    this.name = name
    this.friends = friends
}

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

// 弊端:操作子类原型对象,会影响父类原型对象
Student.prototype = Person.prototype
4、原型式继承(对象继承对象)
var obj = {
    name: "孟美岐"
}
// 方式一
// var info = createObject1(obj);
function createObject1(o) {
    var newObj = {}
    // 相当于newObj.__proto__ = o(可以这么理解,不能这么做)
    Object.setPrototypeOf(newObj, o)
    return newObj
}

// 方式二
// var info = createObject2(obj);
function createObject2(o) {
    function Fn() {
    }

    Fn.prototype = o
    var newObj = new Fn()
    return newObj
}

// 方式三
var info = Object.create(obj);
5、寄生式继承
var personObj = {
    running: function () {
        console.log("running")
    }
}

function createStudent(name) {
    var stu = Object.create(personObj)
    stu.name = name
    stu.studying = function () {
        console.log("studying~")
    }
    return stu
}

var student = createStudent("黄婷婷");
6、寄生组合式继承
// 相当于Object.create()
function createObject(o) {
    function Fn() {
    }

    Fn.prototype = o
    return new Fn()
}

// 2、用于继承方法(配合原型三角图理解,不用考虑父类实例)
function inheritPrototype(SubType, SuperType) {
    SubType.prototype = createObject(SuperType.prototype)
    Object.defineProperty(SubType.prototype, "constructor", {
        value: SubType,
        writable: true,
        configurable: true,
        enumerable: false
    })
}

function Person() {
}

function Student() {
    // 1、用于继承属性
    Person.call(this)
}

inheritPrototype(Student, Person)
7、补充说明Object的方法
var obj = {
    name: "孟美岐",
    age: 18
}
var info = Object.create(obj, {
    address: {
        value: "北京市",
        enumerable: true
    }
})

// 1、hasOwnProperty:对象是否有某一个属于自己的属性(不是在原型上的属性)
console.log(info.hasOwnProperty("address"))
console.log(info.hasOwnProperty("name"))

// 2、in/for in 操作符:判断某个属性是否在某个对象或者对象的原型上
console.log("address" in info)
console.log("name" in info)
for (var key in info) {
    console.log(key)
}

// 3、instanceof:用于检测构造函数的prototype,是否出现在某个实例对象的原型链上
console.log(obj instanceof Object)

// 4、isPrototypeOf:用于检测某个对象,是否出现在某个实例对象的原型链上
console.log(obj.isPrototypeOf(info))
8、Function、Object
* function fun() {} 等价 var fun = new Function("")
* 所有构造函数的__proto__都指向Function的原型对象
      Function.__proto__ -> Function.prototype
      Object.__proto__ -> Function.prototype
* 除了Object原型对象,所有(排除继承)原型对象的__proto__都指向Object的原型对象
      Function.prototype.__proto__ -> Object.prototype
      Object.prototype.__proto__ -> null
posted on 2022-02-23 15:44  一路繁花似锦绣前程  阅读(4)  评论(0编辑  收藏  举报

分类:

技术点:

相关文章: