- 本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
- 这是源码共读的第xx期,链接: juejin.cn/post/708743…
用法
classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'
// lots of arguments of various types
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'
// other falsy values are just ignored
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'
var arr = ['b', { c: true, d: false }];
classNames('a', arr); // => 'a b c'
let buttonType = 'primary';
classNames({ [`btn-${buttonType}`]: true });
源代码
//自执行函数
(function () {
'use strict';
var hasOwn = {}.hasOwnProperty;
function classNames() {
var classes = [];
//遍历传参
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
//过滤null、undefined、false、0等非法字符
if (!arg) continue;
var argType = typeof arg;
//是string或者number类型
if (argType === 'string' || argType === 'number') {
classes.push(arg);
} else if (Array.isArray(arg)) { //是数组
//空数组
if (arg.length) {
//递归遍历数组的内容
var inner = classNames.apply(null, arg);
if (inner) {
classes.push(inner);
}
}
} else if (argType === 'object') { //对象
//({ha:true}).toString() '[object Object]'
//({}).toString() "[object Object]"
//Object.prototype.toString [native code]
//arg.toString.toString() 'function toString() { [native code] }'
//不是空对象的话
//不是原生对象,例如Object.toString();//"function Object() { [native code] }"
//String.toString();//"function String() { [native code] }"
//自定义var person1 = new Person();
//person1.toString();//"[object Object]"
//重写了则结果集合中加入调用toString
if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
classes.push(arg.toString());
continue;
}
for (var key in arg) {
//依次检查对象的每一个自有属性,为真值则加入到结果集合中
if (hasOwn.call(arg, key) && arg[key]) {
classes.push(key);
}
}
}
}
//结果数组使用空格分隔
return classes.join(' ');
}
//导出classnames
if (typeof module !== 'undefined' && module.exports) {
classNames.default = classNames;
module.exports = classNames; //require.js
} else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
// register as 'classnames', consistent with npm package name
define('classnames', [], function () { //CMD
return classNames;
});
} else {
window.classNames = classNames; //挂载到window
}
}());
对象则检查是否重写了toString方法。如果重写了则结果集合中加入调用toString方法后的值,如果没有重写则依次检查对象的每一个自有属性,为真值则加入到结果集合中。
为什么重写toString()?
对象的原始的toString()函数是位于该对象的原型的原型对象中的,我们只要在该对象的原型中新建一个toString()函数就可覆盖对象的原始的toString()函数。
function Person(name, age, gender){
this.name = name;
this.age = age;
this.gender = gender;
}
Person.prototype.toString = function(){
return "Person[name="+this.name+",age="+this.age+",gender="+this.gender+"]";
}
//创建一个Person实例
var p1 = new Person("曹阿瞒",77,"男");
//当我们直接在页面中打印一个对象时,实际上是输出的对象toString()方法的返回值
var result = p1.toString();