本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
1. isUndef
function isUndef (v) {
return v === undefined || v === null
}
let a = null
let b = undefined
console.log(isUndef(a)); // true
console.log(isUndef(b)); //true
2. isDef
function isDef (v) {
return v !== undefined && v !== null
}
与1相反
3. isTrue
function isTrue (v) {
return v === true
}
4. isFalse
function isFalse (v) {
return v === false
}
5. isPrimitive 是否是原始值
function isPrimitive (value) {
return (
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'symbol' ||
typeof value === 'boolean'
)
}
js的基本数据类型有string、number、boolean、symbol、null、undefined、bigint 这边我复习了一下symbol类型和浅浅学习了一下bigint类型
6. isObject
function isObject (obj) {
return obj !== null && typeof obj === 'object'
}
排除了null object、array、function 都为ture
7. toRawType
var _toString = Object.prototype.toString;
function toRawType (value) {
return _toString.call(value).slice(8, -1)
}
可以看一下MDN中对Object.prototype.toString()的描述
复习一下slice方法以及splice方法 这里就不赘述了 e.g 有点忘记splice只能用来操作数组不能操作字符串而slice可以都可以操作
call apply bind 这个我还只知道用法,使用场景啥的都还不知道,有查阅资料但是现在看得不是很懂后续去看
三者都是改变this指向
call、apply参数不同 都可以立即执行
call(xx,参数1,参数1),参数1))
bind第二个参数是数组
bind 第二个参数是数组 返回值是一个函数 需要()调用
8. isPlainObject
function isPlainObject (obj) {
return _toString.call(obj) === '[object Object]'
}
isObject([]) // true
isPlainObject([]) // false
9. isRegExp
function isRegExp (v) {
return _toString.call(v) === '[object RegExp]'
}
检查是否是正则表达式 知道了toRowType 就很好知道这个
这个是最严谨的判断类型的方式
10. isValidArrayIndex
function isValidArrayIndex (val) {
var n = parseFloat(String(val));
return n >= 0 && Math.floor(n) === n && isFinite(val)
}
parseFloat 解析一个参数(必要时候先转换成字符串),并返回一个浮点数
遇到除了+、-、科学计数法e或E、[0-9]、小数点 之外的数字会忽略该字符以及之后的所有字符,返回当前已经解析到的浮点数 第二个小数点的出现也会使得解析停止 会去除首位的空格 parseFloat 解析 BigInt 为 Numbers, 丢失精度。因为末位 n 字符被丢弃。 parseFloat 也可以转换一个已经定义了 toString 或者 valueOf 方法的对象,它返回的值和在调用该方法的结果上调用 parseFloat 值相同。 console.log(parseFloat(Infinity)); //Infinity 推荐使用Number(val) 只要val中存在无效字符就会被转化成NaN
Math.floor() 舍去小数部分 用来判断是不是整数
Math.ceil() 返回大于或等于一个给定数字的最小整数。 (-2.1)=>-2 (2.1)=>3 Math.round() 四舍五入 注意 Math.round() 并不总是舍入到远离 0 的方向(尤其是在负数的小数部分恰好等于 0.5 的情况下
isFinite() 判断数是不是无穷大
11. isPromise
function isPromise (val) {
return (
isDef(val) &&
typeof val.then === 'function' &&
typeof val.catch === 'function'
)
}
// 这个判断promise的方法我自己写了一个不是promise的函数好像好像也可以 有、、疑惑
// 就比如我下面写的例子
let promiseObj = {
then: function() {
console.log('This is then');
},
catch: function() {
console.catch('This is catch')
}
}
function isPromise (val) {
return (
typeof val.then === 'function' &&
typeof val.catch === 'function'
)
}
console.log(isPromise(promiseObj)); // true
12. toString
function toString (val) {
return val == null
? ''
: Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
? JSON.stringify(val, null, 2)
: String(val)
}
13. toNumber
function toNumber (val) {
var n = parseFloat(val);
return isNaN(n) ? val : n
}
如果传入的val不能转换成数字那么就返回val
14. makeMap
function makeMap (
str,
expectsLowerCase
) {
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]; }
}
用了闭包!
Object.create(proto, [propertiesObject]) 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型 可以去mdn看一下语法和描述
// 这个是mdn上例子 我这里使用了一下它的第二个参数 至于第二个参数写法可以去看一下mdn上的Object.defineProperties()的第二个属性
const person = {
isHuman: false,
printIntroduction: function() {
console.log(this);
console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
}
};
const me = Object.create(person,{
age: {
enumerable: true,
writable: true,
configurable: true,
value: 12
}
});
me.name = 'Matthew';
me.isHuman = true;
me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"
Object.create(null)
expectsLowerCase是否忽略大小写
15. isBuiltInTag
/**
* Check if a tag is a built-in tag.
*/
var isBuiltInTag = makeMap('slot,component', true);
16. isReservedAttribute
/**
* Check if an attribute is a reserved attribute.
*/
// 是否是保留的属性
var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is');
17. remove
function remove (arr, item) {
if (arr.length) {
var index = arr.indexOf(item);
if (index > -1) {
return arr.splice(index, 1)
}
}
}
test.js
let arr = [1,2,3,4]
remove(arr,1)
console.log(arr);
splice
- splice返回值是删除元素组成的数组
- splice改变原数组
- splice实际上是替换其第三个参数可以用来替换删除的位置
18.hasOwn
var hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn (obj, key) {
return hasOwnProperty.call(obj, key)
}
检查对象是否存在某个自身的属性 继承的属性返回false
对于为啥要 hasOwnProperty = Object.prototype.hasOwnProperty 是因为
在日常开发中,有某些常见的用法,使得Object.prototype上的方法有时可能不可用或是会被重新定义了。
看下面例子
案例一:被重新定义了
let obj = {
name: 'liuyang',
hasOwnProperty: function() {
return 'kkk'
}
}
console.log(obj.hasOwnProperty('name'));
// output: kkk
// 对象自身有一个hasOwnProperty属性 那么这个方法就会调用它本身的
案例二: Object.crete(null)创建的对象没有从原型上继承任何对象方法
Object.create(null).hasOwnProperty('name')
// output: TypeError: Object.create(...).hasOwnProperty is not a function
19. cached
function cached (fn) {
var cache = Object.create(null);
return (function cachedFn (str) {
var hit = cache[str];
return hit || (cache[str] = fn(str))
})
}
第一次调用的时候 调用fn(str)方法 将值存到cache(str)中 str=>val 以后调用的时候直接从cache对象中拿 效率大大提高
闭包 高阶函数
20. camelize
//连字符转小驼峰
var camelizeRE = /-(\w)/g;
var camelize = cached(function (str) {
return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; })
});
是我第一次遇到 replace的第二个参数用fun 去mdn看了下指定一个函数作为参数 所以这边的 _表示匹配到的字符串 c表示()里的规则匹配到的字符串
// e.g.
console.log(camelize("on-click")) // onClick
// _为_c c为c
21. capitalize
var capitalize = cached(function (str) {
return str.charAt(0).toUpperCase() + str.slice(1)
});
首字母大写
console.log(capitalize('abc')); // Abc
22.hyphenate
var hyphenateRE = /\B([A-Z])/g;
var hyphenate = cached(function (str) {
return str.replace(hyphenateRE, '-$1').toLowerCase()
});
这也是我第一次遇到 replace 第二个参数这种写法 去看了下mdn使用字符串做参数
23. bind
function polyfillBind (fn, ctx) {
function boundFn (a) {
var l = arguments.length;
return l
? l > 1
? fn.apply(ctx, arguments)
: fn.call(ctx, a)
: fn.call(ctx)
}
boundFn._length = fn.length;
return boundFn
}
function nativeBind (fn, ctx) {
return fn.bind(ctx)
}
var bind = Function.prototype.bind
? nativeBind
: polyfillBind;
稍后研究
24. toArray
function toArray (list, start) {
start = start || 0;
var i = list.length - start;
var ret = new Array(i);
while (i--) {
ret[i] = list[i + start];
}
return ret
}
类数组对象转化成数组 还可以指定开始的下标(默认是从0开始)
let obj = {
0: 'ly',
1: 'ltt'
}
console.log(toArray(obj)) // [ly,ltt]
25. extend
function extend (to, _from) {
for (var key in _from) {
to[key] = _from[key];
}
return to
}
将from对象的属性合并到to对象中 如果有属性重合 from覆盖to
let obj1 = {
name: 'liuyang'
}
let obj2 = {
age: '23',
height: 160
}
console.log(extend(obj1,obj2));
console.log(obj1); // 改变
console.log(obj2); // 不改变
26. toObject
function toObject (arr) {
var res = {};
for (var i = 0; i < arr.length; i++) {
if (arr[i]) {
extend(res, arr[i]);
}
}
return res
}
将一个对象数组转换成单个对象
let arr = [
{
a: 1
},
{
b: 2
}
]
console.log(toObject(arr)); // { a: 1, b: 2 }
27. noop
function noop (a, b, c) {}
啥也不干
28. no
var no = function (a, b, c) { return false; };
不管传入啥都返回false
29. identity
var identity = function (_) { return _; };
返回传入的第一个参数
30. genStaticKeys
// Generate a static keys string from compiler modules 从编译器模块中生成静态的字符串
function genStaticKeys (modules) {
return modules.reduce(function (keys, m) {
return keys.concat(m.staticKeys || [])
}, []).join(',')
}
返回modules中所有属性为staticKeys的值组成的字符串 还不清楚该函数在vue中的应用
let arr = [
{
staticKeys: 'name'
},
{
},
{
staticKeys: 'height'
},
{
staticKeys: 'width'
}
]
console.log(genStaticKeys(arr));
31. looseEqual
function looseEqual (a, b) {
if (a === b) { return true }
var isObjectA = isObject(a);
var isObjectB = isObject(b);
if (isObjectA && isObjectB) {
try {
var isArrayA = Array.isArray(a);
var isArrayB = Array.isArray(b);
if (isArrayA && isArrayB) {
return a.length === b.length && a.every(function (e, i) {
return looseEqual(e, b[i])
})
} else if (a instanceof Date && b instanceof Date) {
return a.getTime() === b.getTime()
} else if (!isArrayA && !isArrayB) {
var keysA = Object.keys(a);
var keysB = Object.keys(b);
return keysA.length === keysB.length && keysA.every(function (key) {
return looseEqual(a[key], b[key])
})
} else {
/* istanbul ignore next */
return false
}
} catch (e) {
/* istanbul ignore next */
return false
}
} else if (!isObjectA && !isObjectB) {
return String(a) === String(b)
} else {
return false
}
}
是一个递归方法。如果是基本类型值则直接判断;如果是对象则遍历对象的属性,属性也有可能是引用类型,所以递归判断;如果是数组则遍历数组元素,数组元素可能是引用类型,所以递归判断;如果是日期类型则转成时间戳比较是否相等。
32. looseIndexOf
function looseIndexOf (arr, val) {
for (var i = 0; i < arr.length; i++) {
if (looseEqual(arr[i], val)) { return i }
}
return -1
}
var obj1 = {a:1}
var obj2 = {b:2}
const res = looseIndexOf([obj1, obj2], {a:1})
console.log(res) //0
val 在 arr中是否存在 存在返回下标 不存在则返回-1
33. once
function once (fn) {
var called = false;
return function () {
if (!called) {
called = true;
fn.apply(this, arguments);
}
}
}
闭包 高阶函数 感觉有点妙
// e.g.
function func() {
console.log(111);
}
let a = once(func)
a() //11
a() //
总结
出现的一些知识点已经另外做了笔记 比如类数组、typeof、instanceof等 没有见过缓存函数cache和once让函数仅调用一次的写法
感觉以后还得过几遍 把知识点理解透彻