Object.prototype.toString.call()
返回的数据格式为 [object Object]
类型,然后用slice截取第8位~倒数一位
,得到结果为 Object
var _toString = Object.prototype.toString;
function toRawType (val) {
return _toString.call(val).slice(8, -1)
}
_toString .call({
}) // "[object Object]"
toRawType({
}) // "Object"
_toString .call([]) // "[object Array]"
toRawType([]) // "Array"
_toString .call(true) // "[object Boolean]"
toRawType(true) // "Boolean"
_toString .call(undefined) // "[object Undefined]"
toRawType(undefined) // Undefined
_toString .call(null) // "[object Null]"
toRawType(null) // "Null"
_toString .call(function(){
}) // "[object Function]"
toRawType(function(){
}) // "Function"
vue
中判断
我们写的组件名
是不是html内置标签
的时候,如果用数组类遍历那么将要循环很多次获取结果,如果把数组转为对象
,把标签名设置为对象的key
,那么不用依次遍历查找,只需要查找一次就能获取结果,提高了查找效率
。
function makeMap (str, expectsLowerCase) {
// 构建闭包集合map
var map = Object.create(null)
var list =str.split(',')
for (var i = 0; i < list.length; i++) {
map[list[i]] = true
}
return expectsLowerCase
? function (val) {
return map[val.toLowerCase()]}
: function (val) {
return map[val];}
}
// 利用闭包,每次判断是否是内置标签只需调用isHTMLTag
var isHTMLTag = makeMap('html,body,base,head,link,meta,style,title')
console.log('res', isHTMLTag('body')) // true
这里还没看出来哪里好用,先占个坑,后面再补补
vue
中_createElement
格式化传入的children
的时候用到了simpleNormalizeChildren
函数,原来是为了拍平数组,使二维数组扁平化(只减少一级嵌套)
,类似lodash
中的flatten方法。
想将多维array
降维成一维数组
,可以用lodash
中的flattenDeep方法。
// 先看lodash中的flatten 作用 减少一级array嵌套深度。
let arr = [1, [2, [3, [4]], 5], [20,30]]
_.flatten(arr)
// 得到结果为 [1, 2, [3, [4]], 5, 20, 30]
// vue中 减少一级array嵌套深度
function simpleNormalizeChildren (children) {
for (var i = 0; i < children.length; i++) {
if (Array.isArray(children[i])) {
return Array.prototype.concat.apply([], children)
}
}
return children
}
simpleNormalizeChildren (arr) // [1, 2, [3, [4]], 5, 20, 30]
// 降维成一维数组
function simpleNormalizeChildren (children) {
for (var i = 0; i < children.length; i++) {
if (Array.isArray(children[i])) {
children = Array.prototype.concat.apply([], children)
}
}
return children
}
simpleNormalizeChildren (arr) // [1, 2, 3, 4, 5, 20, 30]
// es6中 等价于
// 降一维
function simpleNormalizeChildren (children) {
return [].concat(...children)
}
simpleNormalizeChildren(arr) // [1, 2, [3, [4]], 5, 20, 30]
// 降维成一维数组
function simpleNormalizeChildren (children) {
let result = []
for (var i = 0; i < children.length; i++) {
if (Array.isArray(children[i])){
result = [].concat(...children)
children = result
}
}
return result
}
simpleNormalizeChildren (arr) // [1, 2, 3, 4, 5, 20, 30]
vue
中利用Object.defineProperty
收集依赖,从而触发更新视图,但是数组却无法监测到数据的变化
,但是为什么数组在使用push pop等
方法的时候可以触发页面更新呢,那是因为vue内部拦截
了这些方法。
// 重写push等方法,然后再把原型指回原方法
var ARRAY_METHOD = ['push', 'pop', 'shift', 'unshift', 'reverse', 'sort', 'splice'];
var array_methods = Object.create(Array.prototype);
ARRAY_METHOD.forEach(method => {
array_methods[method] = function () {
// 拦截方法
console.log('调用的是拦截的 ' + method + ' 方法,进行依赖收集');
return Array.prototype[method].apply(this, arguments)
}
})
var arr = [1, 2, 3]
arr.push(4) // 4 没有打印文字
arr.__proto__ = array_methods // 改变arr的原型
arr.push(4) // 5 调用的是拦截的 unshift 方法,进行依赖收集
vue
中调用Vue.extend
实例化组件,Vue.extend
就是VueComponent构造函数
,而VueComponent
利用Object.create
继承Vue
,所以在平常开发中Vue
和 Vue.extend
区别不是很大。这边主要学习用es5
原生方法实现继承,当然,es6中 class类
直接用extends
继承。
// 继承方法
function inheritPrototype(Son, Father) {
var prototype = Object.create(Father.prototype)
// 下面这一行 我去掉后 也不影响继承 作用是什么
prototype.constructor = Son
// 把Fater.prototype复制给Son.prototype
Son.prototype = prototype
}
function Father(name) {
this.name = name
this.arr = [1, 2, 3]
}
Father.prototype.getName = function() {
console.log(this.name)
}
function Son(name, age) {
// 继承了 除Father.prototype外的内容
Father.call(this, name)
this.age = age
}
inheritPrototype(Son, Father)
Son.prototype.getAge = function() {
console.log(this.age)
}
// 测试
var son1 = new Son("hello", 25)
son1.getName() // hello
son1.getAge() // 25
son1.arr.push(4)
console.log(son1.arr) // [1, 2, 3, 4]
var son2 = new Son("world", 35)
son2.getName() // world
son2.getAge() // 35
console.log(son2.arr) // [1, 2, 3]
es6实现继承
// 父类
class People {
constructor(name) {
this.name = name
}
eat() {
console.log(`${
this.name} eat something`)
}
}
// 子类
class Student extends People {
constructor(name, number) {
super(name)
this.number = number
}
sayHi() {
console.log(`姓名 ${
this.name} 学号 ${
this.number}`)
}
}
// 子类
class Teacher extends People {
constructor(name, major) {
super(name)
this.major = major
}
teach() {
console.log(`${
this.name} 教授 ${
this.major}`)
}
}
// 实例
const xialuo = new Student('夏洛', 100)
console.log(xialuo.name) // 夏洛
console.log(xialuo.number) // 100
xialuo.sayHi() // 姓名 夏洛 学号 100
xialuo.eat() // 夏洛 eat something
// 实例
const wanglaoshi = new Teacher('王老师', '语文')
console.log(wanglaoshi.name) // 王老师
console.log(wanglaoshi.major) // 语文
wanglaoshi.teach() // 王老师 教授 语文
wanglaoshi.eat() // 王老师 eat something
once
方法相对比较简单,直接利用闭包
实现
function once(fn) {
var called = false;
return function() {
if (!called) {
called = true;
fn.apply(this, arguments)
} else {
console.log('函数已执行过一次,不会再执行')
}
}
}
function test() {
console.log('我只会执行一次')
}
var test2 = once(test)
test2() // 我只会执行一次
test2() // 函数已执行过一次,不会再执行
test2() // 函数已执行过一次,不会再执行
返回一个函数
,赋值给 test2判断两个对象
是否相同,主要是判断两个对象包含的值
都是一样的,如果包含的值依然是个对象,则继续递归调用
判断是否相同。
function isObject(obj) {
return obj !== null && typeof obj === 'object'
}
function looseEqual(a, b) {
// 如果是同一个对象 则相同
if (a === b) return true
// 判断是否是对象
const isObjectA = isObject(a)
const isObjectB = isObject(b)
// 两者都是对象
if (isObjectA && isObjectB) {
try {
// 判断是否是数组
const isArrayA = Array.isArray(a)
const isArrayB = Array.isArray(b)
// 两者都是数组
if (isArrayA && isArrayB) {
// 长度得一样 同时每一项要相同 递归调用
return a.length === b.length && a.every((e, i) => {
return looseEqual(e, b[i])
})
} else if (a instanceof Date && b instanceof Date) {
// 如果都是时间对象 则需要保证时间戳相同
return a.getTime() === b.getTime()
} else if (!isArrayA && !isArrayB) {
// 两者都不是数组 则为对象
// 拿到两者的key值 存入数组
const keysA = Object.keys(a)
const keysB = Object.keys(b)
// 对象属性的个数要一样 递归判断每一个值是否相同
return keysA.length === keysB.length && keysA.every(key => {
return looseEqual(a[key], b[key])
})
} else {
return false
}
} catch(e) {
return false
}
} else if (!isObjectA && !isObjectB) {
// 两者都不是对象
// 转成字符串 看值是否一致
return String(a) === String(b)
} else {
return false
}
}
// 例子
let a1 = [1,2,3,{
a:1,b:2,c:[1,2,3]}];
let b1 = [1,2,3,{
a:1,b:2,c:[1,2,3]}];
console.log(looseEqual(a1,b1)); // true
let a2 = [1,2,3,{
a:1,b:2,c:[1,2,3,4]}];
let b2 = [1,2,3,{
a:1,b:2,c:[1,2,3]}];
console.log(looseEqual(a2,b2)); // false
谢谢你阅读到了最后~
期待你关注、收藏、评论、点赞~
感谢
vue源码中值得学习的方法
盘点Vue源码中用到的工具函数