【若川视野 x 源码共读】第26期 | classNames

112 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情

本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。

参考

github.com/JedWatson/c…

www.yuque.com/ruochuan12/…

作用

A simple JavaScript utility for conditionally joining classNames together.

一个简单的 JavaScript 实用程序,用于有条件地将类名连接在一起。

把对象数组等解析成字符串。bool为false的置为空字符串,对象属性是true的就加入,数组打平后判断。

classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'

源码

(function () {
	'use strict';

	var hasOwn = {}.hasOwnProperty;
	// 通过数组收集 参数 ,对各种参数进行改造,后面用join拼起来
	function classNames() {
		var classes = [];

		for (var i = 0; i < arguments.length; i++) {
			var arg = arguments[i];
            //如果布尔判断为空直接跳过判断下一个
			if (!arg) continue;

			var argType = typeof arg;
	//字符串和数字直接加
			if (argType === 'string' || argType === 'number') {
				classes.push(arg);
    // 如果是数组,递归加      
    //举个例子,function aa(a){if(Array.isArray(a)){return aa(...a) }else {return a}} [['a']]=>'a'
			} else if (Array.isArray(arg)) {
				if (arg.length) {
					var inner = classNames.apply(null, arg);
					if (inner) {
						classes.push(inner);
					}
				}
			} else if (argType === 'object') {
                //如是对象,判断对象的toString方法有没有改过,正常的话
				if (arg.toString === Object.prototype.toString) {
                    //遍历属性,对象hasOwnProperty方法判断是否是对象属性而非原型上的且值为真,加入
                    
					for (var key in arg) {
						if (hasOwn.call(arg, key) && arg[key]) {
							classes.push(key);
						}
					}
				} else {
					classes.push(arg.toString());
				}
			}
		}

		return classes.join(' ');
	}
	//兼容导出环境,node 
	if (typeof module !== 'undefined' && module.exports) {
		classNames.default = classNames;
		module.exports = classNames;
        //amd
	} else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
		// register as 'classnames', consistent with npm package name
		define('classnames', [], function () {
			return classNames;
		});
	} else {
        //兼容浏览器环境
		window.classNames = classNames;
	}
    //但是没看到怎么兼容esm的,没有export xx呀。。
}());
逻辑:
1.创个数组
2.遍历参数,假值跳过,数字或字符直接加,对象遍历且值为真就加,数组递归且值为真就加
3.数组join成字符串

Alternate dedupe version

github1s.com/JedWatson/c…

classNames('foo', { foo: false, bar: true }); // => 'bar'

重复数据可以覆盖删除,主要是下面的代码,对象值为false也存resultSet数组里,最后遍历数组为true的值再join。

	function _parseObject (resultSet, object) {
			if (object.toString === Object.prototype.toString) {
				for (var k in object) {
					if (hasOwn.call(object, k)) {
						resultSet[k] = !!object[k];
					}
				}
			} else {
				resultSet[object.toString()] = true;
			}
		}

Alternate bind version

映射,取对象值不取属性

github1s.com/JedWatson/c…

var classNames = require('classnames/bind');
var styles = {
  foo: 'abc',
  bar: 'def',
  baz: 'xyz'
};

var cx = classNames.bind(styles);

var className = cx('foo', ['bar'], { baz: true }); // => "abc def xyz"
//映射,取对象值不取属性
//源码
	function classNames () {
		var classes = [];

		for (var i = 0; i < arguments.length; i++) {
			var arg = arguments[i];
			if (!arg) continue;

			var argType = typeof arg;
	//bind指定了this为 styles ,那么在原先基础 classes.push(arg); 改成 classes.push( this[arg] );
			if (argType === 'string' || argType === 'number') {
				classes.push(this && this[arg] || arg);
			} else if (Array.isArray(arg)) {
				classes.push(classNames.apply(this, arg));
			} else if (argType === 'object') {
				if (arg.toString === Object.prototype.toString) {
					for (var key in arg) {
						if (hasOwn.call(arg, key) && arg[key]) {
							classes.push(this && this[key] || key);
						}
					}
				} else {
					classes.push(arg.toString());
				}
			}
		}

		return classes.join(' ');
	}