1. 学习任务和目标
- 任务发布: 【若川视野 x 源码共读】第26期 | classnames
- 仓库地址:github.com/JedWatson/c…
- 学会 classnames 的用法
- 了解classnames 的原理
2. 学习过程
classnames 是一个简单的工具库,用于有条件地将类名连接在一起。
2.1 如何使用
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 });
// 在 react 组件中,可以将其设置为 state,通过绑定事件的回调函数对其进行更改,使其类名动态更改、样式动态更改
class Button extends React.Component {
// ...
render () {
var btnClass = 'btn';
if (this.state.isPressed) btnClass += ' btn-pressed';
else if (this.state.isHovered) btnClass += ' btn-over';
return <button className={btnClass}>{this.props.label}</button>;
}
}
// 结合 react 的对象形式
var classNames = require('classnames');
class Button extends React.Component {
// ...
render () {
var btnClass = classNames({
btn: true,
'btn-pressed': this.state.isPressed,
'btn-over': !this.state.isPressed && this.state.isHovered
});
return <button className={btnClass}>{this.props.label}</button>;
}
}
// 结合 props
var btnClass = classNames('btn', this.props.className, {
'btn-pressed': this.state.isPressed,
'btn-over': !this.state.isPressed && this.state.isHovered
});
2.2 classnames 源码
/*!
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/
/* global define */
// 用了一个 自执行的函数 来包裹整个作用域 避免变量污染冲突
(function () {
'use strict'; // 严格模式
var hasOwn = {}.hasOwnProperty; // 判断对象的属性是否属于自己本身,而不是在原型链上面找到的
function classNames() {
var classes = []; // 存储最后类名合集的数组
for (var i = 0; i < arguments.length; i++) { // 传入参数不限制数量,用到参数对象 arguments
var arg = arguments[i]; // 遍历 arguments 拿到每一项
if (!arg) continue; // 如果该项的值为 undefined、null之类的就直接跳过
var argType = typeof arg; // 获取该项的类型
// 字符串或者数字之类的直接加入 classes 中
if (argType === 'string' || argType === 'number') {
classes.push(arg);
} else if (Array.isArray(arg)) {
if (arg.length) {
// 针对数组中的每一项都需要进行判断是否能够加入 classes 中,所以利用 递归+apply 达到数组扁平化的效果
var inner = classNames.apply(null, arg);
if (inner) { // 递归调用返回的不是空字符串的话就加入 classes
classes.push(inner);
}
}
} else if (argType === 'object') { // 对象的情况下
// 如果自带的 toString 方法 和 Object 的不一样
if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
classes.push(arg.toString()); // 用自身自定义的 toString 方法
continue;
}
for (var key in arg) {
if (hasOwn.call(arg, key) && arg[key]) { // 如果该属性是自身的 && value 为 true(或者说 可以转变为 true)
classes.push(key);
}
}
}
}
return classes.join(' ');
}
// 支持各种导出方式
if (typeof module !== 'undefined' && module.exports) { // CommonJS
classNames.default = classNames;
module.exports = classNames;
// AMD, 通过判断是否又 define 方法以及 define.amd 是否为 object
} 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;
}
}());
3. 简单总结
3.1 二维数组扁平化的方法
// 使用 apply 方法,apply方法接收的参数是一个包含了若干个参数的数组(也正是因为这样,可以用于数组扁平化)
let a1=[[12,21],[1,2,3],[2,3,4]];
function turn(arr){
return [].concat.apply([],arr);
}
console.log('res: ', turn(a1)); // [12, 21, 1, 2, 3, 2, 3, 4]
// 方法2:使用递归
let res_arr = [];
let fun = (arr) => {
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
fun(arr[i])
} else {
res_arr.push(arr[i])
}
}
}
fun([[12,21],[1,2,3],[2,3,4]]);
console.log(res_arr) // [12, 21, 1, 2, 3, 2, 3, 4]
其他方法:flat() 该方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
var newArray = arr.flat([depth]);
// depth 可选,指定要提取嵌套数组的深度,默认值为 1。
3.2 求数组中的最大最小值
let arr = [2,3,1,4,5,6]
Math.max.apply(null, arr); // 获取数组中最大值 6
Math.min.apply(null, arr); // 获取数组中最小值 1