前端知识梳理 —— js

内置类型

js中分为七种内置类型 七种内置类型又分为基本类型和对象类型
基本类型有六种:null undefined boolean string number symbol
其中js中数字类型是浮点型 没有整型 并且浮点型是基于IEEE 754标准实现 在使用过程中会遇到精度不准的bug
NaN也属于number类型 并且NaN不等于自身
对象类型是引用类型 所以会存在浅拷贝和深拷贝的问题
let a = { name: 'hjx' };
let b = a;
b.name = 'yym';
console.log(a.name)  //yym
//因为b拷贝的是a对象的引用地址 导致b和a是同一个引用地址 所以b的改变会影响到a 

Typeof

typeof 对于基本类型 除了 null 都可以显示正确的类型
typeof 对于对象类型 除了函数都会显示 object
对于 null 来说 虽然它是基本类型 但是会显示 object 这是一个存在很久的 bug
原因:因为在js的最初版本中 使用的是32位系统 为了性能考虑使用低位存储变量的类型信息 
000开头代表对象 然而 null 的表示全为零 所以将它错误判断为 object 虽然现在的内部类型代码已经改变了 
但是对于这个 bug 却一直流传下来
如果我们想要获取一个变量的正确类型 可以通过Object.prototype.toString.call(x)的方式 这样我们就可以得到类型[object Type]的字符串

类型转换

1. 转Boolean类型
在条件判断时 除了undefined null false '' NaN 0 -0 其他所有的值都转成true 包括所有对象

2. 基本类型转数字类型
false -> 0 
true -> 1
null -> 0
undefined -> NaN
'' -> 0
如果字符只包含数字 将其转化为十进制数字 '1' -> 1 '01' -> 1
如果字符包含有效浮点格式 将其转化为对应浮点数值
如果包含有效十六进制格式 '0xf' -> 15
其他字符都转化为NaN 例如 '1h'  '1.2.2'

3. 对象转化成基本类型
先调用valueOf 然后调用toString
由于Array重写了toString 这也是为什么[]转换为Number类型为0的原因

4. 加法中的类型转换
在加法中 字符的优先级最高 其次是数字 
如果加法运算时存在字符 那么将会把其他所有转化为字符进行运算 
1 + '1' + [1]  //'111'
这里发现运算中存在字符 把每一项转化为字符然后相加 1 -> '1'
[1] -> '1' 最后结果为'111'
如果加法中不存在字符 存在数字 那么将会把其他所有转化为数字进行运算
如果加法中只存在对象类型 那么将对象转化为基础类型进行运算
[1, 2] + [1, 2]  // '1212'

5. 双等中的类型转换
undefined == null //true
双等中数字优先 也就是说如果存在数字 则将另一方转化为数值进行比较
如果双方都为对象类型 则将其转化为基础类型进行比较

原型
在这里插入图片描述

每个对象都有__proto__属性 该属性指向创建该对象构造函数的原型prototype 
其实这个属性指向的是[[prototype]] 但是[[prototype]]是内部属性 我们不能直接访问 
所以使用__proto__来访问
对象可以通过__proto__来寻找不属于该对象上的属性 _proto__将对象连接起来组成原型链

new

new的工作原理
1. 新生成一个对象
2. 链接到原型
3. 绑定this
4. 返回对象
我们可以尝试实现new的功能
function create() {//第一个参数传构造函数 之后为参数
    //创建空对象
    const obj = {};
    const Con = [].shift.call(arguments);
    //链接原型
    obj.__proto__ = Con.prototype;
    //绑定this
    Con.apply(obj, arguments);
    //返会新对象
    return obj;
}
另外new Foo() 的优先级要大于 new Foo
例:
new Foo.getName()  等价于  new (Foo.getName())
new Foo().getName()  等价于  (new Foo()).getName()

instanceof

instanceof的工作原理是看对象的原型链上是否存在该类型的原型
例:[] instanceof Object //true
instanceof 会看[]的原型链上是否存在Object.prototype 如果存在 返会true 否则返回false
接下我们手动实现一个instanceof
function myInstanceof(obj, type) {
    prototype = type.prototype;
    let proto = obj.__proto__;
    while (true) {
        if (proto === prototype) {
            return true;
        }
        if (proto === null) {
        	return false;
        }
        proto = proto.__proto__
    }
}

this

this的指向取决于函数的调用 而与函数的声明无关
直接调用(这里也可以理解成在window作用域中调用)所以this指向window
间接调用 谁调用this便指向谁

执行上下文

执行上下文分三种
全局执行上下文
函数执行上下文
eval执行上下文
执行上下文在代码执行前生成 每个执行上下文中都有三个重要的属性 分别是变量对象VO 活动对象AO(函数执行前产生) 作用域链[[Scope]] 
console.log(a); //undefined
var a = 1;
之所以会打印出undefined 就是因为执行上下文在代码执行前生成
globalContext.VO = { a: undefined };
这也是为什么存在声明提升的原因

深浅拷贝

由于给一个变量赋值对象时保存的是对象的引用
const a = { name: 'hjx' };
const b = a;
b.name = 'yym';
console.log(a.name);  // 'yym'
所以会导致b的改变会使得a也发生改变 这也导致对象拷贝存在深浅拷贝

浅拷贝
Object.assin() 或 扩展运算符

深拷贝
JSON.parser(JSON.stringify(obj))
但是这个方法存在局限性:
1. 会忽略undefined
2. 不能序列化函数
3. 不能解决循环引用的对象
function deepClone(origin, target) {
    for (let key in origin) {
        if (origin.hasOwnProterty(key)) {

            if (typeof origin[key] === 'object' && origin[key] !== null) {
                target[key] = Array.isArray(origin[key]) ? [] : {};
                deepClone(origin[key], target[key]);
            }else {
                target[key] = origin[key];
            }

        }
    }
}

模块化

1. es6模块化
在有babel的情况下 我们可以直接使用es6的模块化
//a.js
export const obj = { name: 'hjx' };
export function foo() {}
//b.js 
export default function () {}

import { obj, foo } from 'a.js'
import xxx from 'b.js'

2. CommonJS
CommonJS是node独有的规范 浏览器中使用就需要用到Browserify解析了
// a.js
module.exports = { a: 1 }
//or 
exports.a = 1
//b.js
const module = require('./a.js');
module.a // 1

3. AMD
AMD是由RequireJS提出的 我没用过

防抖

function debounce(handler, delay) {
    let timer = null;
    return function () {
        clearTimeout(timer)
        timer = setTimeout(() => {
            handler.call(this);
        }, delay)
    }
}

节流

function throttle(handler, wait) {
    let startTime = 0;
    return function () {
        let endTime = new Date().getTime(); 
        if (endTime - startTime > wait) {
            handler.call(this);
        }
        startTime = endTime;
    }
}

继承

Sub.prototype = Object.create(Super.prototype, {
    constructor: {
        value: Sub,
        enumerable: false,
        writeable: true,
        configurable: true
    }
})
//or
function inherit(Origin, Target) {
    function F() {};
    F.prototype = Origin.prototype;
    Target.prototype = new F();
    Target.prototype.constructor = Target;
}

call apply bind

call 和 apply 都是用来改变this指向 唯一不同点就是传参方式不同
1. 模拟实现call
Function.prototype.myCall = function (context, ...arg) {
    context = context || window;
    context.fn = this;
    const result = context.fn(...arg);
    delete context.fn;
    return result;
}
2. 模拟实现apply
Function.prototype.myApply = function (context, ...arg) {
    context = context || window;
    context.fn = this;
    const result = context.fn(arg);
    delete context.fn;
    return result;
}
3. 模拟实现bind
Function.prototype.myBind = function (context, ...arg) {
    const originFn = this;
    return function F() {
        if (this instanceof F) {
            //因为返回的是函数 所以可能存在new的情况
            return new originFn(...arg, ...arguments);
        }
        return originFn.apply(context, arg.concat(arguments));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值