html css 部分
1.flex 弹性布局: 语法篇
2.BFC (块级格式化上下文): 语法篇
3.实现左右宽度固定中间自适应的布局方法(1.定位 2.浮动 3.flex 4.grid)
4.盒模型(标准模型、IE怪异盒模型)
5.css 选择器优先级
6.伪类、伪元素的区别
js 部分
一、new 运算符的原理:
1)一个继承自 Foo.prototype 的新对象被创建。
2)使用指定的参数调用构造函数 Foo,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数时,Foo 不带任何参数调用的情况。
3)如果构造函数返回了一个“对象”,那么这个对象会取代整个 new 出来的结果。如果构造函数没有返回对象,那么 new 出来的结果为步骤(1)创建的对象
function mockNew() { // es5的实现方式
const Foo = Array.prototype.shift.call(arguments) // 相当于[].shift.call(arguments)
const obj = {}
obj.__proto__ = Foo.prototype
const r = Foo.apply(obj, arguments)
return r instanceof Object ? r : obj
}
// 代码测试
function Animal(type) {
this.type = type
return 1
// return function() {}
// return { name: 'animal' }
}
Animal.prototype.shout = function() {
console.log('shout')
}
const animal = mockNew(Animal, '动物')
二、Javascript 中数据类型判断的几种方法:
基本数据类型 (number、string、boolean、null、undefined、symbol)
引用数据类型 (object、array、function)
typeof判断类型返回值为(number、string、boolean、object、undefined、symbol、function)。
typeof 123 // 'number'
typeof '123' // 'string'
typeof null // 'object'
typeof [] // 'object'
typeof new Date // 'object'
typeof function() {} // 'function'
2.想要区分某个对象属于哪种内置类型,单纯使用 typeof 是不行的。我们可以通过Object.prototype.toString 方法来判断:
Object.prototype.toString.call([]);// "[object Array]"
Object.prototype.toString.call({});// "[object Object]"
缺点: 不能校验变量是否属于某个对象的实例,需要用 instanceof 方法。
3.instanceof 运算符的原理:
应用场景: 用于判断变量是否属于某个对象的实例,根据原型链进行搜寻。内部会调用**[Symbol.hasInstance]** 方法去做判断。
// 使用方式
function A () {}
const a = new A()
a instanceof A // true
a instanceof Object // true
// 实现原理
function Myinstanceof(ins, Foo) {
ins = ins.__proto__
Foo = Foo.prototype
while(true) {
if (ins === null) return false
if (ins === Foo) {
return true
}
ins = ins.__proto__
}
}
但如果基本类型使用 instanceof 时,判断会失效,如下面的示例:
'123' instanceof String // false
// 相当于下面的调用方式
String[Symbol.hasInstance]('123')
魔改方式:
class String {
static [Symbol.hasInstance](str) {
return typeof str === 'string'
}
}
String[Symbol.hasInstance]('123') // true
4.constructor方式:
用 constructor 来判断类型看起来是完美的。然而,如果我创建一个对象,并更改它的原型,这种方式也变得不可靠了。
因此,当要修改对象的
proptotype 时,一定要设置 constructor 指向其构造函数。
三、Javascript 中深浅拷贝:
浅拷贝: 对象共用一个内存地址,对象的变化相互影响。。 如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。
1.数组 slice 浅拷贝。
2.... 扩展运算符: 一层是深拷贝,多层是浅拷贝。
Object.assign浅拷贝。
深拷贝: 拷贝前后没有关系。
1.JSON.parse(JSON.stringify(str)) 会忽略掉undefined,不能序列化函数,不能解决循环引用的对象。
2.手写递归深拷贝。
// 1.普通版本
function deepClone(obj) {
if (obj == null) return obj
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
if (typeof obj !== 'object') return obj // 基本类型不需要拷贝
const cloneObj = new obj.constructor
for (let key in obj) {
if (obj.hasOwnProperty(key)) { // 仅拷贝实例属性
cloneObj[key] = deepClone(obj[key]) // 实现递归拷贝关键
}
}
return cloneObj
}
const obj = {a:1, b:{ c: 2 }}
const cloneObj = deepClone(obj)
obj.b.c = 100
console.log(cloneObj) // { a: 1, b: { c: 2 } }
// 2.解决循环引用版本
function deepClone(obj, hash = new WeakMap) { // WeakMap为弱引用,会自动被垃圾回收机制回收。
if (obj == null) return obj
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
if (typeof obj !== 'object') return obj // 基本类型不需要拷贝
`if (hash.has(obj)) return hash.get(obj) `
const cloneObj = new obj.constructor
`hash.set(obj, cloneObj)`
for (let key in obj) {
if (obj.hasOwnProperty(key)) { // 仅拷贝实例属性
cloneObj[key] = deepClone(obj[key], hash) // 实现递归拷贝关键
}
}
return cloneObj
}
const obj = {a:1, b:{ c: 2 }}
obj.o = obj // 循环引用(自己引用自己)
const cloneObj = deepClone(obj)
obj.b.c = 100
console.log(cloneObj) // { a: 1, b: { c: 2 }, o: [Circular] }
四、Javascript 中的原型、原型链
原型: prototypo 原型链: proto
每一个函数都有一个 prototypo 属性,每一个对象都有 __proto__ 属性。
判断属性存在实例上需要用 hasOwnProperty 方法。
判断属性存在实例或原型上需要用 in 方法。
function Animal(type) {this.type = type}
Animal.prototype.shout = function() {}
const animal = new Animal('dog')
console.log(animal.hasOwnProperty('type')) // true
console.log('shout' in animal) // true
五、Javascript 中的函数柯理化
六、Javascript 中的call、apply、bind
1.call、apply 都可改变当前函数 this 指向,并且让当前函数执行。只不过 apply 第二个参数是一个数组,里面放参数, call 从第二个参数开始,依次放置函数参数。
// 用法
const obj = {a: 1}
function fn() {
console.log(this, arguments)
}
fn.call(obj, 1, 2, 3)
fn.apply(obj, [1, 2, 3])
2.多次调用 call 函数时,如: fn.call.call.call(fn2),this 会指向 fn2,并且执行 fn2。
// call 代码实现方式
Function.prototype.myCall = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = Object(context) || window
context.fn = this
const args = [...arguments].slice(1)
const result = context.fn(...args)
delete context.fn
return result
}
const obj = {a: 1}
function fn() {
console.log(this, arguments)
}
function f2() {
console.log(this, arguments)
}
fn.myCall(obj, 1, 2, 3)
fn.myCall.myCall.myCall(fn2, 1, 2, 3) // this变为fn2, 并且让fn2执行。
// apply 代码实现方式(仅处理参数和 call 有区别)
Function.prototype.myApply = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = Object(context) || window
context.fn = this
let result
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
bind方法创建一个新的函数(高阶函数),当新函数被调用时,将其this关键字设置为提供的值。 当new调用新函数时,context参数无效,new操作符修改this指向的优先级更高。
// bind 代码实现方式
Function.prototype.MyBind = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
const that = this
const args = [...arguments].slice(1)
const fBound = function(...arguments) {
that.apply(this instanceof fBound ? this : context, args.concat(arguments))
}
// fBound.prototype = Object.create(this.prototype)
// ** Object.create 原理 **
function F() {}
F.prototype = this.prototype
fBound.prototype = new F()
return fBound
}
const obj = {a: 1}
function fn() {
console.log(this)
}
fn.prototype.b = 2
let fn3 = fn.MyBind(obj)
let a = new fn3() // fn {}
fn3() // { a: 1 }
七、Javascript 中的防抖、节流
八、Javascript 中的Object.create原理(重要)
Object.create 接收对象作为参数(这个对象将作为新对象的原型对象),创建一个新对象并返回。
const obj = {a: 1}
let newObj = Object.create(obj)
console.log(newObj.__proto__ === obj)
// 实现原理
Object.create = function(obj) {
function F() {} // 1.创建一个构造函数
F.prototype = obj // 2.将构造函数的原型指向obj
return new F() // 3.通过new创建对象并返回
}
九、手写Javascript 中的EventEmitter
十、手写Javascript 中数组的多个方法
1.reduce MDN链接
// reduce用法
const arr = [1, 2, 3]
const sum = arr.reduce((prev, curr, index, array) => {
return prev + curr
}, 10) // 16
// 代码实现
Array.prototype.MyReduce = function (callback, initialValue) {
if (this.length === 0) {
if (!initialValue) {
throw new TypeError('Reduce of empty array with no initial value')
} else {
return initialValue
}
}
let prev = initialValue ? initialValue : this[0]
let idx = initialValue ? 0 : 1
for(let i = idx; i < this.length; i++) {
prev = callback(prev, this[i], i, this)
}
return prev
}
2.map MDN链接
// map用法
const arr = [1, 2, 3]
const doubleArr = arr.map((item, index, array) => {
return item * 2
})
// 代码实现
Array.prototype.MyMap = function (callback) {
if (!Array.isArray(this) || this.length === 0 || typeof callback !== 'function') {
return []
}
const result = []
for (let i = 0; i < this.length; i++) {
result.push(callback(this[i], i, this))
}
return result
}
十一、手写一个完整的 Promise
十二、Javascript 中的 EventLoop
十三、Javascript 中的 高阶函数、compose等
未完待续,敬请期待,持续更新...