web前端之零碎知识点、字符串、数组、基础知识

MENU

  • 前言
  • 对象的构造函数和类
  • 向Number内置类的原型上扩展方法
  • 判断是否给实例设置属性name
  • Promise笔试题-02
  • prototype和__proto__的笔试题
  • JavaScript引用类型值值操和运算符优先级比较--笔试
  • 原型与原型链--笔试-05
  • 闭包--笔试-11
  • 浅浅克隆和浅克隆(clone)--01
  • 深克隆(clone)--01
  • 作用域-笔试
  • 数据类型转换
  • ==(两等) 和 !(非) 的优先级比较
  • 访问器属性-13
  • 变量提升案例
  • var、let和const的区别
  • 一维数组升为二维数组
  • promise的使用
  • 字符串去重(以逗号隔开的字符串)
  • 字符串去重
  • vue+transition-group实现拖动排序
  • 数组对象排序
  • 正则表达式实现简单模板数据填充
  • defineReactive函数,利用闭包封装Object.defineProperty()
  • JavaScript获取字符串(string)第一个字符
  • JavaScript递归实现数组扁平化(数组降维)
  • JavaScript中number和~~比较、API
  • JavaScript实现单词、字母排序
  • JavaScript实现分组排序单词、单词分组排序、不能实现中文首拼音排序
  • typescri枚举、enum
  • typescript类型别名、限制值的大小
  • typescript使用class关键字定义一个类、static、readonly
  • typescript中class的constructor(构造函数)
  • typescript中abstractClass(抽象类)、extends、abstract
  • typescript中的接口、type、interface
  • typescript封装属性、public、private、protected、constructor、get、set、extends
  • vue3响应式数据的判断、isRef、isReactive、isReadonly、isProxy、ref、reactive、readonly
  • JavaScript之逻辑运算、||、&&
  • JavaScript之函数的实际参数(实参)和形式参数(形参)、arguments(实参)、(a, b, c, d实参)、字面量定义具名函数
  • JavaScript之使用字面量定义具名函数,且实现递归
  • JavaScript之形参默认值、实参、undefined占位
  • JavaScript校验特殊字符、indexOf、substr
  • JavaScript之合并两个对象、Object、assign
  • JavaScript之中划线、下划线隔开的变量转为小驼峰、横杠、replace、substr、toUpperCase、正则、RegExp
  • 深克隆(clone)--02
  • 函数柯理化-01
  • 柯理化函数之参数复用
  • 面向对象的三大特性
  • class继承
  • 闭包--节流函数--笔试-10
  • 构造函数继承-1
  • JavaScript数据类型有哪些?如何存储值?
  • null和undefined的区别是什么?
  • 图片懒加载
  • JavaScript之对象 (object) 解构
  • 检测一个对象是不是数组
  • JavaScript之new操作符
  • 原型实例继承-2
  • 原型直接继承-3
  • ES6的Class继承、super、extends、constructor-4
  • 作用域-作用域链
  • 原型与原型链-总结
  • 闭包的定义
  • 原型实例继承和原型直接继承的区别?
  • 渲染(绑定)数据的时候嵌套两层(需要点(.)两次)的时候报错的解决方案
  • 事件队列-笔试题
  • JavaScript两种解决跨域的方法
  • JavaScript之回调函数
  • vue插槽
  • JavaScript之单词首字母转大写
  • Vue之ref的使用
  • Vue之组件间传参
  • JavaScript之字符串方法与对象
  • JavaScript 中的执行上下文和执行栈
  • JavaScript实现添加班级和离开班级功能、通知、加入、删除、push、indexOf、splice
  • vue的过滤器和计算属性实现全选与非全选
  • DOM节点
  • JavaScript之函数作用域(scopes)和函数作用域链(scope chain)
  • html之标签元素设置自定义属性、setAttribute、getAttribute
  • web前端之vue在父组件中调用子组件的方法函数
  • JavaScript之对象打点的访问方式
  • vue返回刷新
  • vue实现菜单右键
  • vue+html5+原生dom+原生JavaScript实现跨区域拖放
  • vue实现跨区域拖放
  • ES6模块化
  • vue实现滚动条点击切换距离、滚动条隐藏样式
  • vue3的生命周期
  • vue3手写isRef、isReactive、isReadonly、isProxy
  • vue3手写ref、深的ref
  • vue3手写shallowRef、浅的ref
  • vue+mousedown实现全屏拖动,全屏投掷
  • vue+element中的InfiniteScroll无限滚动之实现分页请求数据、滚动加载请求接口
  • vue3customRef
  • vue3readonly与shallowReadonly
  • vue3toRaw与markRaw
  • JavaScript实现数值转汉字大写、价格
  • JavaScript实现价格输入控制并翻译为中文大写、结合
  • 无需循环就可以把数据添加到数组相应位置、动态往数组对象中添加数据、对象类型数据的妙用
  • vue之this.$set和Vue.set的使用
  • vue3手写reactive、深的劫持、深的监视、深的响应数据
  • JavaScript之整数翻转、包括负整数、Number、String、substr、substring
  • JavaScript之数据改造、后端只返回有数据的时段,没数据的时段需要前端自己补全、isArray、throw、forEach、hasOwnProperty、call、for in、push
  • JavaScript实现文字转表情包、RegExp、正则、matchAll、createDocumentFragment、append、createElement、append、remove、slic
  • JavaScript之原型链
  • JavaScript中值在各种场景的转换规则
  • 程序员(web前端开发工程师)、手机号码、二进制、十进制、构造函数、substr、parseInt、prototype、length
  • JavaScript实现构造函数的封装
  • 待定


前言

每一个大标题,曾经都是一篇文章,此文章内容比较散乱。


对象的构造函数和类

创建对象

// 使用字面量创建对象
let starL = {
    Uname: '刘邦',
    Uage: 30
};
console.log(starL);

// 使用构造函数创建对象
function Star(Uname, Uage) {
	this.Uname = Uname;
	this.Uage = Uage;
};
let starW = new Star('王之涣', 23); // 实例化对象
console.log(starW);

创建类

class Poet {
	// 类的共有属性放到 constructor 里面,
	// constructor 是构造器或者构造函数
	constructor(Uname, age) {
		this.Uname = Uname;
		this.age = age;
    };
    // 注意: 方法与方法之间不需要添加逗号
    humVerse(song) {
		console.log(this.Uname + '吟' + song);
    };
};
var PoetW = new Poet('王安石', 26);
console.log(PoetW);
PoetW.humVerse('元日');

向Number内置类的原型上扩展方法

编写plus和minus实现如下需求

// anonymous: 匿名函数的名称
// 闭包的作用是防止函数跟全局函数有冲突
// 函数自调方式
// ~ function() {}();
// (function() {})()
~ function anonymous(proto) {
	// 封装 plus 函数 和 minus 函数 共同部分
    // 使用函数表达式方式创建函数是为了保证函数提升和
    // 防止在创建函数前调用函数
	const checkNum = function checkNum(num) {
		// 强制转换为 Number 类型
		num = Number(num);
		// 如果 num 不是有效数字
		if (isNaN(num)) {
			// 给 num 赋值为 0
			num = 0;
		}
		// 返回 num
		return num;
	};
	
	// 函数表达式
    // 把匿名函数改为具名函数,在全局通过函数名无法访问此函数
	proto.plus = function plus(num) {
		// 在函数内部可以调用此函数
        // 调用方式 Number.prototype.plus()
        // 既不跟外部函数名字冲突
        // 又可以通过特殊方式调用
        // => this: 要操作的那个数字实例(对象) 
        // => 返回 Number 类的实例,实现链式写法
		return this + checkNum(num);
	};
	
	proto.minus = function minus(num) {
		return this - checkNum(num);
	};
}(Number.prototype);

let n = 10;
let m = n.plus(10).minus(5);
console.log(m); // => 15 (10+10-5)
// 向 Number 内置类(原型)上扩展方法
// 创建一个数据类型值:
// 1.字面量方式
// 2.构造函数方式
// 不论哪一种方式,创建出来的结果都是所属类的实例
// => 基本数据类型两种创建方式是不一样的: 字面量创建的是基本类型值,
// 构造函数方式创建的是引用类型值
// let x = 10; // 字面量方式
// let y = new Number(10); // 构造函数方式
// console.log(y.valueof() === x);
// => 对象结果的原始值是基本类型数字 10

判断是否给实例设置属性name

function C1(name) {
	// => name: undefined 没有给实例设置私有的属性name
	if (name) this.name = name;
}

function C2(name) {
	// => 给实例设置私有属性 name this.name = undefind
	this.name = name;
}

function C3(name) {
	// => 给实例设置私有属性 name this.name = undefind
	// 因为 name 的值为 undefined
	// 所以走 || 后面的代码
	this.name = name || 'join';
}

C1.prototype.name = 'Tom';
C2.prototype.name = 'Tom';
C3.prototype.name = 'Tom';

console.log((new C1().name) + (new C2().name) + (new C3().name));

// new C1().name
// 没有传参 函数里面的 name 为 undefined
// 实例里面没有 name 找原型上的 'Tom'

// new C2().name 
// 没有传参 函数里面的的 name 为 undefined
// 但是函数确实添加了 name 属性 只是值为 undefined 而已
// 所以找私有属性 name 的值为 undefined

// new C3().name
// 没有传参 函数里面的 name 为 undefined
// 但是函数确实添加了 name 属性 只是值为 'join'
// 所以私有属性 name 的值为 'join'

// 最终结果为 'Tomundefinedjoin'

Promise笔试题-02

console.log(1);
// 宏仁务 2
setTimeout(_ => {
	console.log(2);
}, 1000);

async function fn() {
	console.log(3);
	// 宏仁务 3
	setTimeout(_ => {
		console.log(4);
	}, 20);
	
	// 结果为失败
	// 所以 微任务 1 
	// 报错永远不会执行
	return Promise.reject();
}

async function run() {
	console.log(5);
	await fn();
	// 微任务 1
	console.log(6);
}
run();

// 需要执行 150 MS 左右
for (let i = 0; i < 90000000; i++) {}

// 宏仁务 4
setTimeout(_ => {
	console.log(7);
	
	// 立即执行
	new Promise(resolve => {
		console.log(8);
		// 微任务 5
		resolve();
	}).then(_ => {
		console.log(9);
	})
}, 0);

console.log(10);

// 1
// 5
// 3
// 10
// Uncaught (in promise) undefined
// 7
// 8
// 9
// 4
// 2

prototype和__proto__的笔试题

function Fn() {
	// 代码字符串
	// 谁 new 就是谁的私有属性
	this.x = 100;
	this.y = 200;
	this.getX = function() {
		console.log(this.x);
	};
	// 每个函数都有 prototype 对象
	// 所以 prototype 是一个堆 Fn.prototype
	// Fn.prototype 对象也带 __proto__ 属性
	// 这个对象上有 constructor
	// constructor 指向函数本身
};

// 把 getX 挂载到 Fn 的原型对象上
// 生成一个新的堆内存
// 属于公共属性
Fn.prototype.getX = function() {
	console.log(this.x);
};

// 把 getY 挂载到 Fn 的原型对象上
// 生成一个新的堆内存
// 属于公共属性
Fn.prototype.getY = function() {
	console.log(this.y);
};

// 括号可加可不不加
// 都是创建类的实例
// 创建实例对象 f1 / f2 (堆)
// 同时创建 x: 100 y: 200  getX: function() { ... }
// 都是这个实例的私有属性
// 通过 this 创建的变量和对象都属于私有属性
// 实例都带 __proto__ 属性
// __proto__ 所属类的原型 prototype
// 通过原型链往上找都属于 实例(Fn.prototype) 的共有属性
let f1 = new Fn();
let f2 = new Fn;

 // Object内置类(堆)
// 有自己的 prototype 原型对象 
// prototype 指向 Object.prototype 原型对象
// 有自己的 __proto__ 属性
// 有自己的 constructor 属性
// constructor 指向 Object
// Object.prototype 的 __proto__ 指向的是 null

// 实例指向的堆内存不同,如果自己私有就不会往原型类上查找
// 因为这个 getX 函数自己私有
// 所以两个实例的 getX 方法不相等
console.log(f1.getX === f2.getX); // false

// 因为 getY 这个函数不是自己私有
// 所以往原型链上查找,结果相等
// 原因就是他们的 __proto__ 都指向 Fn.prototype 原型对象
console.log(f1.getY === f2.getY); // true

// 通过原型链查找到的是公共的 getY 
// 类似于 f1.getY === f2.getY
console.log(f1.__proto__.getY === Fn.prototype.getY); // true

// f1.__proto__.getX 找的是 Fn.prototype 公共属性
// f2.getX 因为自己私有
// 所以不相等
console.log(f1.__proto__.getX === f2.getX); // false

// f1.getX 属于自己私有的属性
// Fn.prototype.getX 本身就是公共属性
// 所以不相等
console.log(f1.getX === Fn.prototype.getX); // false

// f1 自己身上没有 constructor 
// 往原型对象上查找
// 又因为 constructor 指向 函数本身
// 所以结果就是 Fn 函数
console.log(f1.constructor); // Fn 函数

// Fn.prototype 的 __proto__ 指向的是 Object.prototy 
// 因为 Object.prototy 的 constructor 指向 Object 本身
// 所以是结果 Object
console.log(Fn.prototype.__proto__.constructor); // ƒ Object() { [native code] }

// f1.getX() 执行
// 并且 f1.getX 是属于自己私有
// 所以输出 100
// 函数执行前面有点
f1.getX(); // 100

// f1.__proto__.getX(); 
// 直接查找的是原型对象上的 getX()
// 原型上的是 console.log(this.x);
// 因为 f1.__proto__.getX(); getX() 前面有点
// 所以 相当于 f1.__proto__.x
// f1.__proto__.x 直接跳过自己(f1 实例对象(堆))往原型对象上查找
// 此时原型对象上也没有,再往 Object.prototype 对象上查找
// Object 对象上也没有
// 最后得到的是 undefined
f1.__proto__.getX(); // undefined

// 与 f1.getX(); 相同
f2.getY(); // 200
        
// Fn.prototype.getY(); 
// 直接查找的是原型对象上的 getY()
// 原型上的是 console.log(this.y);
// 因为 Fn.prototype.getY(); getY() 前面有点
// 所以 相当于 Fn.prototype.y
// Fn.prototype.y 直接跳过自己(f2 实例对象(堆))往原型对象上查找
// 此时原型对象上也没有,再往 Object.prototype 对象上查找
// Object 对象上也没有
// 最后得到的是 undefined
Fn.prototype.getY(); // undefined

JavaScript引用类型值值操和运算符优先级比较–笔试

let a = { n : 1 };
let b = a;
a.x = a = { n: 2 };    
// 如果改为这样
// a = a.x = { n: 2 }; 
// 结果还是一样
// JavaScript 中点(.)比 等号 (=) 的优先级高

console.log(a.x); // undefined
// a.x = a = { n: 2 }; 
// 这段代码可以写成这样子
// a.x = { n: 2 };
// a = { n: 2 };
// 因为 a 是引用类型数据。
// 所以当 a.x = { n: 2 };时引用值的地址改变;
// 此时全局的 let a = { n: 1, x: { n: 2 } };
// 接着又执行 a = { n: 2 };时 a 引用地址已经改变。
// 最后输出的结果就是 : undefined

console.log(b.x); // { n: 2 }
// 经过上面 a 的操作,
// 全局的 a 值已经变为 a = { n: 1, x: { n: 2 } };
// 而此时 b 引用的地址一直没有改变
// 所以输出结果为 { n: 2 }

原型与原型链–笔试-05

function A() {};

A.prototype.n = 1;
var b = new A(); // b 实例对象已经建立原型连接
// 原型对象指向被改变,不会切断 b 实例对象的的指向
A.prototype = {
    n: 2,
    m: 3
};
var c = new A(); // c 实例对象将根据新的原型建立连接
console.log(b.n, b.m); // 1 undefined 这里拿到是改变 prototype 之前的堆数据
console.log(c.n, c.m); // 2 3 这里拿到是改变 prototype 之后的堆数据
// 此题生成了两个堆内存
// 并且两个堆内存都有自己的实例存储 => b c

闭包–笔试-11

function fun() { 
	var n = 9; 
	// js 中强行给一个未声明的变量赋值,
	// 程序不会报错
	// 并且会自动在全局创建此变量
	add = function() { 
		n++;
	}; 
	return function() { 
		console.log(n); 
	}; 
};

// 把 fun() 执行的结果赋值给 fn 变量
var fn = fun();

// 此处调用的是全局的 add 函数,
// 因为全局的 add 函数作用域链引用着 fun 函数作用域对象
// 所以修改的是 fun 里面变量的值
add();
fn(); // 10

// 把 fun() 执行的结果赋值给 fn2 变量
// 注意:这里的 fn2 所引用的是 fun() 执行后的地址
// 所以 fn 和 fn2 变量使用的地址是不同,结果也不相同
var fn2 = fun();
fn2(); // 9
add();
add();
fn2(); // 11
fn(); // 10
add();
fn(); // 10

浅浅克隆和浅克隆(clone)–01

let oldObject = {
	name: '西施',
	age: 26,
	height: '163.30',
	obj: {
		city: '成都',
		street: '玉林路'
 	},
	array: ['赵雷', '嬴政', '天明']
};    

// 浅克隆 -- 只能处理一层数据结构的克隆
// 深克隆 -- 可处理超过一层或多层数据结构的克隆
function clone(oldObject) {
	let newObject = {};
	for (let key in oldObject) {
		newObject[key] = oldObject[key];
	};
	return newObject;
};

// 浅浅克隆
// 引用同一个地址
let newData = oldObject;
console.log(oldObject === newData); // true

// 浅克隆成功
console.log(oldObject == clone(oldObject)); // false

let cloneData = clone(oldObject);

// 第一层可以克隆成功
cloneData.age = 30;
console.log('cloneData:', cloneData.age); // 30
console.log('oldObject:', oldObject.age); // 26

// 第二层 对象
cloneData.obj.city = '杭州';
console.log('cloneData:', cloneData.obj.city); // 杭州
console.log('oldObject:', oldObject.obj.city); // 杭州

// 第二层 数组
cloneData.array[1] = '荆轲';
console.log('cloneData:', cloneData.array[1]); // 荆轲
console.log('oldObject:', oldObject.array[1]); // 荆轲

深克隆(clone)–01

let oldObject = {
	name: '西施',
	age: 26,
	height: '163.30',
	score: null,
	friends: ['李白', '杜牧', '李商隐'],
	oldObj: {
		city: '广州',
		area: '越秀区',
		street: '明信路'
	}
};

function clone(oldObject) {
	// 第一步:创建一个空对象
	let newObject = {};
	// 第二步:判断值是否为空
	// 如果给的值里面有 null 就直接返回 null
	// 如果不进行处理会输出 {} 空对象
	if (oldObject === null) {
		return null;
	}
	// 第三步:判断是否是数组
	// 如果是数组需要处理,
	// 如果不处理数组会转为类数组对象
	// { 0: "李白", 1: "杜牧", 2: "李商隐" }
	if ({}.toString.call(oldObject) === '[object Array]') {
		var newArray = [];
		// 数组克隆
		// 使用 splice() 方法实现
		// splice() 里面什么也没写,
		// 表示从数组第一项截取到末尾
		newArray = oldObject.slice();
		return newArray;
	}
	// 第四步:使用 for in 循环实现克隆
	for (let key in oldObject) {
		// 如果原对象属性值是原始类型,
		// 可以直接赋值
		// 原始类型值是直接复制副本
		if (typeof oldObject[key] !== 'object') {
			newObject[key] = oldObject[key];
		} else {
			// 当前属性不是原始类型值,
			// 再次调用(类似与递归) clone 函数,
			// 继续复制当前属性的值
			newObject[key] = clone(oldObject[key]);
		}
	}
	return newObject;
};

// 如果是 false 说明克隆成功
console.log(oldObject === clone(oldObject)); // false

let cloneData = clone(oldObject);

// 修改克隆后的第一层 age 的值
cloneData.age = 30;

// 修改克隆后第二层 数组的值
cloneData.friends[2] = '杜甫';

// 修改克隆后的二层 city 的值
cloneData.oldObj.city = '武汉';

// 深克隆,第一层两个值的地址已经完全不一样了
console.log('oldObject:', oldObject.age); // 26
console.log('newObject:', cloneData.age); // 30

// 深克隆,第二层 数组 两个值的地址已经完全不一样了
console.log('oldObject:', oldObject.friends[2]); // 李商隐
console.log('newObject:', cloneData.friends[2]); // 杜甫

// 深克隆,第二层 对象 两个值的地址已经完全不一样了
console.log('oldObject:', oldObject.oldObj.city); // 广州
console.log('newObject:', cloneData.oldObj.city); // 武汉

作用域-笔试

var a = 0, 
	b = 0;
	
function A(a) {
	// A(1)执行
	// 1.A函数被重置为
	// function A(b) {
		// console.log(a + b++);
	// };
	// 2.执行打印console.log(a++),
	// 因为此时的a是形参,形参的值是传入的1,
	// 所以打印1,而且形参a执行了++,此时值为2
	A = function (b) {
		// A(2)调用情况
		// 这个a采用的是闭包中的a,而不是全局中的a,
		// 所以是2,b是形参也是2,所以打印的是4
		// b使用的是传入的形参而不是全局中的b
		console.log(a + b++);
	};
	// 调用函数A(1)的时候执行这一行代码,
	// 并且把函数重置
	console.log(a++);
};
A(1); 
// 1
A(2); 
// 4

数据类型转换

在JavaScript中类型转换只有三种情况。
1、转换为布尔值,调用Boolean()方法。
2、转换为数字,调用Number()、parseInt()和parseFloat()方法。
3、转换为字符串,调用.toString()或者String()方法。


==(两等) 和 !(非) 的优先级比较

console.log([] == ![]); // true

1、!(非) 的优先级高于==(两等) ,右边[]是true,取返等于false。
2、一个引用类型和一个值去比较,会把引用类型转化成值类型。所以,[] => 0 。
3、最后0 == false的结果是true 。


访问器属性-13

let attribute = {
	id: 11,
	sname: '名字'
};

// 给 attribute 对象添加两个属性:_age 和 age
// age 作为保镖保护 _age
// 第一步
Object.defineProperties(attribute, {
	// 先添加一个半隐藏的 _age
	_age: {
		// 值
		value: 26,
		// 可以改
		writable: true,
		// 半隐藏
		enumerable: false,
		// 双保险
		configurable: false,
	},

	// 再为 _age 添加一个保镖 -- 访问器属性
	age: {
		get: function() {
			return this._age;
		},
		set: function(value) {
			if (value >= 18 && value <= 65) {
				this._age = value;
			} else {
				throw Error('年龄必须介于18~65之间。');
			}
		},
		enumerable: true,
		configurable: false
	}
});

console.log(attribute);
// {id: 11, sname: "名字", _age: 26}

console.log(attribute.age);
// 26

console.log(attribute._age);
// 26

attribute.age = 27;
console.log(attribute.age);
// 27

attribute.age = 16;
console.log(attribute.age);
// Uncaught Error: 年龄必须介于18~65之间。

变量提升案例

function funHoisting() {
	console.log(1);
};

funHoisting(); // 2

function funHoisting() {
	console.log(2);
};

funHoisting(); // 2

var funHoisting = 100;
// 在函数提升的时候 var fun 对程序没有任何影响,
// 等于没写
funHoisting();
// Uncaught TypeError: funHoisting is not a function

var、let和const的区别

1、var声明的变量会挂载在window上,而let和const声明的变量不会。
2、var声明变量存在变量提升,let和const不存在变量提升。
3、let和const声明形成块作用域。
4、同一作用域下var可以声明同名变量,而let和const不可以。


一维数组升为二维数组

function arrTrans(num, arr) {
	// 参数 num : 决定二维数组的个数
	// 参数 arr : 源数组
	const iconsArr = [];
	
	arr.forEach((item, index) => {
		// floor() 向下取整
		const page = Math.floor(index / num);
		// 向数组 iconsArr 添加数组
		// 数组下标就是 page
		if (!iconsArr[page]) iconsArr[page] = [];
		
		iconsArr[page].push(item);
	});
	
	return iconsArr;
}

console.log(arrTrans(5, [1, 1, 2, 3, 4, 5, 6, 7, 8]));
// [[1, 1, 2, 3, 4], [5, 6, 7, 8]]

promise的使用

function func1() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("func1 1000");
    }, 1000);
  });
};

function func2() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("func1 2000");
    }, 2000);
  });
};

function func3() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("func1 3000");
    }, 3000);
  });
};

func1()
  .then((result) => {
    console.log(result);
    return func3();
  })
  .then((result) => {
    console.log(result);
    return func2();
  })
  .then((result) => {
    console.log(result);
  });

字符串去重(以逗号隔开的字符串)

function stringDuplicateRemoval(str) {
	let obj = {},
		arr = str.split(","),
		result = [],
		i = 0;
		
	for (let i = 0; i < arr.length; i++) obj[arr[i]] = 1;

	for (result[i++] in obj);
	
	return result;
}

console.log(stringDuplicateRemoval('1,2,3,3'));
// [1,2,3]

console.log(stringDuplicateRemoval('1,2,3,3').toString());
// '1,2,3'

字符串去重

function stringDuplicateRemoval(str) {
	let newStr = "";
	
	for (let i = 0; i < str.length; i++) {
		if (newStr.indexOf(str[i]) == -1) newStr += str[i];
	}
	
	return newStr;
}

console.log(this.stringDuplicateRemoval("110120140"));
// 1024

vue+transition-group实现拖动排序

<transition-group id='app' name="drog" tag="ul">
	<div draggable="true" v-for="(item, index) in lists" @dragstart="dragStart($event, index)" @dragover="allowDrop" @drop="drop($event, index)" v-bind:key="item">{{item}}div>
transition-group>

new Vue({
    el: '#app',
    data: {
        lists: ['1: apple', '2: banana', '3: orange', '4: melon']
    },
    
	methods: {
        // 取消默认行为
        allowDrop(e){
            e.preventDefault();
        },
        
        // 开始拖动
        dragStart(e, index){
            let tar = e.target;
            e.dataTransfer.setData('Text', index);
            if (tar.tagName.toLowerCase() == 'li') {
                // console.log('drag start')
                // console.log('drag Index: ' + index)
            }
        },
        
        // 放置
        drop(e, index){
            this.allowDrop(e);
            // console.log('drop index: ' + index);
            //使用一个新数组重新排序后赋给原变量
            let arr = this.lists.concat([]),
                dragIndex = e.dataTransfer.getData('Text');
                temp = arr.splice(dragIndex, 1);
            arr.splice(index, 0, temp[0]);
            // console.log('sort');
            this.lists = arr;
        }
    }
});

数组对象排序

let arr = [
	{
		id: 1,
		weather:'晴'
	}, 
	{
		id: 2,
		weather:'小雨'
	}, 
	{
		id: 3,
		weather:'大雨'
	}, 
	{
		id: 4,
		weather:'小雨'
	}, 
	{
		id: 5,
		weather:'阴'
	}, 
	{
		id: 6,
		weather:'雪'
	}
];

function sortData(a, b) {
	return b.id - a.id;
}

arr.sort(sortData);

console.log(arr);

正则表达式实现简单模板数据填充

let templateStr = '

我买了一棵{{thing}},花了{{money}}元,好{{mood}}

'
; let data = { thing: '白菜', money: 5, mood: '激动' }; // 最简单的模板引擎的实现机理, // 利用的是正则表达式中的 replace() 方法。 // replace() 的第二个参数可以是一个函数, // 这个函数提供捕获的东西的参数,就是$1 // 结合 data 对象,即可进行智能的替换 function render(templateStr, data) { return templateStr.replace(/\{\{(\w+)\}\}/g, function (findStr, $1) { return data[$1]; }); } console.log(render(templateStr, data)); //

我买了一棵白菜,花了5元,好激动


defineReactive函数,利用闭包封装Object.defineProperty()

function defineReactive(data, key, val) {
	Object.defineProperty(data, key, {
		// 可枚举
		enumerable: true,
		// 可以被配置,比如可以被 delete
		configurable: true,
		// getter  
		get() { 
			return val;
		},
		// setter
		set(newValue) {
			if (val === newValue) return false;
			val = newValue;
		}
	});
};
let obj = {};
defineReactive(obj, 'a', 10); // 设置 a 属性
console.log(obj.a); // 10 访问 a 的值
obj.a = 100; // 改变 a 的值
console.log(obj.a); // 100 访问改变后 a 的值

JavaScript获取字符串(string)第一个字符

let stringVal = "web半晨";
// 方式一
console.log(stringVal.charAt(0));
// 方式二
console.log(stringVal.substring(0, 1));
// 方式三
console.log(stringVal.substr(0, 1));

JavaScript递归实现数组扁平化(数组降维)

let recursionData = [
	{
		id: 1,
		name: "一级",
		children: [
			{
				id: 2,
				name: "二级-1",
				children: [
					{
						id: 7,
						name: "三级-1",
						children: [
							{
								id: 10,
								name: "四级-1",
							},
						],
					},
					{
						id: 8,
						name: "三级-2",
					},
				],
			},
			{
				id: 3,
				name: "二级-2",
				children: [
					{
						id: 5,
						name: "三级-3",
					},
					{
						id: 6,
						name: "三级-4",
					},
				],
			},
			{
				id: 4,
				name: "二级-3",
				children: [
					{
						id: 9,
						name: "三级-5",
						children: [
							{
								id: 11,
								name: "四级-2",
							},
						],
					},
				],
			},
		],
	},
],
	arr = [];

function funRecursion(list) {
	for (let i in list) {
		arr.push({
			id: list[i].id,
			name: list[i].name,
		});

		if (list[i].children) funRecursion(list[i].children);
	}
	
	return arr;
}

console.log(funRecursion(recursionData));
// [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]

console.log(arr.map((item) => item.id).sort((a, b) => a - b));
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

JavaScript中number和~~比较、API

function replaceNumber() {
  console.log(Number(null)); // 0
  console.log(~~null); // 0
  console.log("--------------------------------------------------------------");

  console.log(Number(undefined)); // NaN
  console.log(~~undefined); // 0
  console.log("--------------------------------------------------------------");

  console.log(Number(123)); // 123
  console.log(~~123); // 123
  console.log("--------------------------------------------------------------");

  console.log(Number("123")); // 123
  console.log(~~"123"); // 123
  console.log("--------------------------------------------------------------");

  console.log(Number("abc")); // NaN
  console.log(~~"abc"); // 0
  console.log("--------------------------------------------------------------");

  console.log(Number(NaN)); // NaN
  console.log(~~NaN); // 0
}

replaceNumber();

JavaScript实现单词、字母排序

function alphabeticSorting(params) {
	return params.sort((a, b) => {
		let x = a.word,
			y = b.word;
			
		return x > y ? 1 : x < y ? -1 : 0;
	});
}

let words = [
	{
		id: 1,
		word: "absolute",
	},
	{
		id: 2,
		word: "every",
	},
	{
		id: 3,
		word: "display",
	},
	{
		id: 4,
		word: "border",
	},
	{
		id: 5,
		word: "class",
	},
	{
		id: 6,
		word: "background",
	},
	{
		id: 7,
		word: "delete",
	},
];

console.log(alphabeticSorting(words));

JavaScript实现分组排序单词、单词分组排序、不能实现中文首拼音排序

function groupSortWords(params) {
    let grouping = {},
        afterGrouping = [];

    params.forEach(item => {
    	// 单词首字母转大写
        let capital = item.word.replace(/\b[a-zA-Z]+\b/g, items => {
            return items.charAt(0).toUpperCase();
        });

        // grouping[capital] 使用了对象不能有同一属性的原理
        // 相当于去重
        if (grouping[capital]) {
            grouping[capital].arr.push(item);
        } else {
            grouping[capital] = {
                groupingName: `${capital}`,
                arr: [item]
            }
        }
    });

    // 把对象变为数组
    for (const key in grouping) {
        if (Object.hasOwnProperty.call(grouping, key)) {
            const itemKye = grouping[key];
            afterGrouping.push(itemKye);
        }
    }

    // 外层数组排序
    afterGrouping = afterGrouping.sort((a, b) => {
        let x = a.groupingName,
            y = b.groupingName;
        return x > y ? 1 : x < y ? -1 : 0;
    });

    // 内层数组排序
    afterGrouping = afterGrouping.map(item => {
        item.arr = item.arr.sort((a, b) => {
            let x = a.word,
                y = b.word;
            return x > y ? 1 : x < y ? -1 : 0;
        });
        return item;
    })

    // 最终返回
    return afterGrouping;
}

// 数据源
let words = [
    {
        id: 1652156,
        word: "absolute",
    },
    {
        id: 2365256,
        word: "every",
    },
    {
        id: 3156258,
        word: "display",
    },
    {
        id: 4695845,
        word: "border",
    },
    {
        id: 5125369,
        word: "class",
    },
    {
        id: 6985485,
        word: "background",
    },
    {
        id: 7156895,
        word: "delete",
    },
    {
        id: 8789651,
        word: "color",
    },
    {
        id: 9369529,
        word: "application",
    },
    {
        id: 1031562,
        word: "length",
    },
];

// 调用函数
console.log(groupSortWords(words));

typescri枚举、enum

enum Gender {
    Male,
    Female
};

let i: { name: string, gender: Gender };
i = { 
	name: '孙悟空', 
	gender: Gender.Male // 'male'
};

console.log(i.gender === Gender.Male); // true

typescript类型别名、限制值的大小

type myType = 1 | 2 | 3 | 4 | 5;
let k: myType;
let l: myType;
let m: myType;

k = 2; // 正常
l = 6; // 报错
m = 3; // 正常

typescript使用class关键字定义一个类、static、readonly

// 使用class关键字来定义一个类
// 类对象中主要包含了两个部分:属性和方法
class Person {
	// 01----------------------------------
    // 直接定义的属性是实例属性,需要通过对象的实例去访问
    a = 'a';

    // 02----------------------------------
    // 使用static开头的属性是静态属性(类属性),可以直接通过类去访问
    static b: number = 18;

    // 03----------------------------------
    // readonly开头的属性表示一个只读的属性无法修改
    readonly c: string = '半晨';

    // 04----------------------------------
    // 静态只读属性
    static readonly d: string = '舒冬';

    // 05----------------------------------
    // 直接定义方法
    e() {
        console.log('直接定义方法');
        // 直接定义方法
    }

    // 05----------------------------------
    // 定义静态方法
    static f() {
        console.log('定义静态方法');
        // 定义静态方法
    }
}

const person = new Person();

console.log('Person实例:', person);
// Person实例: Person {a: "a", c: "半晨"}

// 01------------------------------------------------
// 直接定义的属性是实例属性,需要通过对象的实例去访问
console.log('实例属性:', person.a);
// 实例属性: a
// console.log('实例属性:', Person.a);
// 实例属性: undefined 
// 类型“typeof Person”上不存在属性“a”。

// 02------------------------------------------------
// 类属性(静态属性)
console.log('类属性(静态属性):', Person.b);
// 类属性(静态属性): 18
// console.log('类属性(静态属性):', person.b);
// 类属性(静态属性): undefined
// 属性“b”在类型“Person”上不存在。你的意思是改为访问静态成员“Person.b”吗?

// 03------------------------------------------------
// readonly开头的属性表示一个只读的属性无法修改
console.log('只读属性:', person.c);
// 只读属性: 半晨
// person.c = '哈哈';
// 无法分配到 "c" ,因为它是只读属性。

// 04------------------------------------------------
// 静态只读属性
console.log('静态只读属性:', Person.d);
// 静态只读属性: 舒冬
// Person.d = '哈哈';
// 无法分配到 "d" ,因为它是只读属性。

// 05------------------------------------------------
// 直接定义方法
person.e();
// Person.e();
// 类型“typeof Person”上不存在属性“e”。

// 05------------------------------------------------
// 直接定义方法
Person.f();
// person.f();
// 属性“f”在类型“Person”上不存在。你的意思是改为访问静态成员“Person.f”吗?

typescript中class的constructor(构造函数)

class Dog {
    // 01--------------------------------------------------------
    // name = '旺财';
    // age = 7;

    // 02--------------------------------------------------------
    // name: string;
    // 属性“name”没有初始化表达式,且未在构造函数中明确赋值。
    // age: number;
    // 属性“age”没有初始化表达式,且未在构造函数中明确赋值。

    // 构造函数会在对象创建时执行
    constructor(name: string, age: number) {
        // 在实例方法中,this就表示当前当前的实例
        // 在构造函数中当前对象就是当前新建的那个对象
        // 可以通过this向新建的对象中添加属性
        this.name = name;
        this.age = age;
    }

    bark() {
        console.log('汪汪汪');
        // 汪汪汪

        // 在方法中可以通过this来表示当前调用方法的对象
        return this.name;
    }
}

// 错误示例--------------------------------------------------------
// const dog1 = new Dog();
// const dog2 = new Dog();

// 01--------------------------------------------------------
// console.log('dog1:', dog1);
// // dog1: Dog {name: "旺财", age: 7}
// console.log('dog2:', dog2);
// // dog2: Dog {name: "旺财", age: 7}

// 02--------------------------------------------------------
// console.log('dog1:', dog1);
// // dog1: Dog {}
// console.log('dog2:', dog2);
// // dog2: Dog {}

// 正确示例--------------------------------------------------------
const dog1 = new Dog('小黑', 6);
const dog2 = new Dog('小白', 7);

console.log('dog1:', dog1);
// dog1: Dog {name: "小黑", age: 6}
console.log('dog2:', dog2);
// dog2: Dog {name: "小白", age: 7}

// 03--------------------------------------------------------
// 调用类中的方法
console.log('dog1:', dog1.bark());
// dog1: 小黑
console.log('dog2:', dog2.bark());
// dog1: 小白

typescript中abstractClass(抽象类)、extends、abstract

// 自执行函数的作用是形成单独模块(块作用域),
// 防止此文件的变量或方法与其他文件的属性或方法冲突
(function () {
    // 以abstract开头的类是抽象类,
    // 抽象类和其他类区别不大,只是不能用来创建对象,
    // 也就是不能new Animal()的意思。
    // 抽象类就是专门用来被继承的类
    // 抽象类中可以添加抽象方法

    abstract class Animal {
        name: string;

        constructor(name: string) {
            this.name = name;
        }

        // 定义一个抽象方法
        // 抽象方法使用abstract开头,没有方法体
        // 抽象方法只能定义在抽象类中,子类必须对抽象方法进行重写
        // void没有返回值(返回值为空)
        abstract sayHello(): void;
    }

    class Dog extends Animal {
        sayHello() {
            console.log('汪汪汪汪!');
        }
    }

    // 非抽象类“Cat”不会实现继承自“Animal”类的抽象成员“sayHello”。
    class Cat extends Animal {
        // sayHello() {
        //     console.log('喵喵喵喵!');
        // }
    }

    const dog = new Dog('旺财');
    const cat = new Cat('喵喵');

    dog.sayHello();
    cat.sayHello();
})();

typescript中的接口、type、interface

// 自执行函数的作用是形成单独模块(块作用域),
// 防止此文件的变量或方法与其他文件的属性或方法冲突
(function () {
    // 描述一个对象的类型
    type myType = {
        name: string,
        age: number
    };
    const obj: myType = {
        name: 'sss',
        age: 111,
    };
    console.log('myType:', obj);
    // myType: {name: "sss", age: 111}

    // 接口用来定义一个类结构
    // 用来定义一个类中应该包含哪些属性和方法
    // 同时接口也可以当成类型声明去使用
    // 接口可以声明相同的接口名称
    // 只是接口会打散合并
    interface myInterface {
        name: string;
        age: number;
    }

    interface myInterface {
        gender: string;
    }

    // 如果属性个数对应不上会报错
    // 说明了接口是可以定义相同接口名称
    // 并且接口会打散合并
    const objs: myInterface = {
        name: 'sss',
        age: 111,
        gender: '男'
    };
    console.log('myInterface:', objs);
    // myInterface: {name: "sss", age: 111, gender: "男"}

    // 接口可以在定义类的时候去限制类的结构
    // 接口中的所有的属性都不能有实际的值
    // 接口只定义对象的结构,而不考虑实际值
    // 在接口中所有的方法都是抽象方法
    interface myInter {
        name: string;

        sayHello(): void;
    }

    // 定义类时,可以使类去实现一个接口
    // 实现接口就是使类满足接口的要求
    class MyClass implements myInter {
        name: string;

        constructor(name: string) {
            this.name = name;
        }

        sayHello() {
            console.log('大家好~~');
            // 大家好~~
            console.log(this.name);
            // 半晨
        }
    }
    let myclass = new MyClass('半晨');
    myclass.sayHello();
})();

typescript封装属性、public、private、protected、constructor、get、set、extends

// 自执行函数的作用是形成单独模块(块作用域),
// 防止此文件的变量或方法与其他文件的属性或方法冲突
(function () {
    // 可以任意修改类中属性的值
    class ArbitrarilyEdit {
        name: string;
        age: number;

        constructor(name: string, age: number) {
            this.name = name;
            this.age = age;
        }
    }

    let arbitrarilyEdit = new ArbitrarilyEdit('半晨', 24);

    // 在对象中直接设置属性
    // 属性可以任意的被修改
    // 属性可以任意被修改将会导致对象中的数据变得非常不安全
    console.log('before-arbitrarilyEdit:', arbitrarilyEdit);
    // before-arbitrarilyEdit: ArbitrarilyEdit {name: "半晨", age: 24}
    arbitrarilyEdit.name = '舒冬';
    arbitrarilyEdit.age = -33;
    console.log('after-arbitrarilyEdit:', arbitrarilyEdit);
    // arbitrarilyEdit: ArbitrarilyEdit {name: "舒冬", age: -33}

    // 定义一个不可以任意修改类中值的类
    class Person {
        // typescript可以在属性前添加属性的修饰符
        // public 修饰的属性可以在任意位置访问(修改) 默认值
        // private 私有属性,私有属性只能在类内部进行访问(修改)
        // 通过在类中添加方法使得私有属性可以被外部访问
        // protected 受包含的属性,只能在当前类和当前类的子类中访问(修改)
        private _name: string;
        private _age: number;

        constructor(name: string, age: number) {
            this._name = name;
            this._age = age;
        }

        // getter方法用来读取属性
        // setter方法用来设置属性
        // 它们被称为属性的存取器

        // 定义方法,用来获取name属性
        getName() {
            return this._name;
        }

        // 定义方法,用来设置name属性
        setName(value: string) {
            this._name = value;
        }

        getAge() {
            return this._age;
        }

        setAge(value: number) {
            // 判断年龄是否合法
            if (value >= 0) {
                this._age = value;
            }
        }

        get name() {
            return this._name;
        }

        set name(value) {
            this._name = value;
        }

        get age() {
            return this._age;
        }

        set age(value) {
            if (value >= 0) {
                this._age = value
            }
        }
    }

    const per = new Person('半晨', 18);

    console.log('before-per:', per);
    // before-per: Person {_name: "半晨", _age: 18}
    per._name = '舒冬';
    per._age = -36;
    console.log('after-per:', per);
    // after-per: Person {_name: "舒冬", _age: -36}
    // 此时是可以编译通过
    // 但是_name和_age会出现下波浪线提示错误

    // 定义方法,获取name属性
    console.log('getName:', per.getName());
    // getName: 舒冬
    // 定义方法,设置name属性
    per.setName('苏檀');
    console.log('setName:', per.getName());
    // setName: 苏檀

    // 定义方法,获取age属性
    console.log('getAge:', per.getAge());
    // getAge: -36
    // 定义方法,设置age属性
    // 此处无法修改原先赋值为 -36 的值
    per.setAge(-16);
    console.log('setAge:', per.getAge());
    // setAge: -36

    // 使用自带的get和set方法
    console.log('before-getName:', per.name);
    // before-getName: 苏檀
    console.log('before-age:', per.age);
    // before-age: -36
    per.name = '宁毅';
    per.age = 36;
    console.log('after-getName:', per.name);
    // after-getName: 宁毅
    console.log('after-age:', per.age);
    // after-age: 36

    // ----------------------------------------------------------
    class A {
        // protected 受包含的属性,只能在当前类和当前类的子类中访问(修改)
        protected num: number;

        constructor(num: number) {
            this.num = num;
        }
    }

    class B extends A {
        test() {
            console.log(this.num);
            // 33
        }
    }

    const b = new B(3436);
    console.log('before-b:', b);
    // before-b: B {num: 3436}
    b.num = 33;
    // 属性“num”受保护,只能在类“A”及其子类中访问。
    console.log('after-b:', b);
    // after-b: B {num: 33}
    // 本来是不应该修改的,
    // 但是编译时没有限制报错不能生成文件导致结果是可以修改
    b.test();

    // ----------------------------------------------------------
    // 方式一和方式二是一样的效果
    // class C {
    //     name: string;
    //     age: number

    //     // 可以直接将属性定义在构造函数中
    //     constructor(name: string, age: number) {
    //         this.name = name;
    //         this.age = age;
    //     }
    // }

    // 方式二和方式一是一样的效果
    class C {
        // 可以直接将属性定义在构造函数中
        constructor(public name: string, public age: number) {
            console.log(name, age);
            // xxx 111
        }
    }

    const c = new C('xxx', 111);
    console.log('c:', c);
    // c: C {name: "xxx", age: 111}
})();

vue3响应式数据的判断、isRef、isReactive、isReadonly、isProxy、ref、reactive、readonly

import {
  defineComponent,
  isProxy,
  isReactive,
  isReadonly,
  isRef,
  reactive,
  readonly,
  ref,
} from "vue";
export default defineComponent({
  name: "App",
  // isRef: 检查一个值是否为一个 ref 对象
  // isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
  // isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
  // isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理

  setup() {
    // isRef: 检查一个值是否为一个 ref 对象
    console.log(isRef(ref({}))); // true
    // isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
    console.log(isReactive(reactive({}))); // true
    // isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
    console.log(isReadonly(readonly({}))); // true
    // isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
    console.log(isProxy(readonly({}))); // true
    console.log(isProxy(reactive({}))); // true

    return {};
  },
});

JavaScript之逻辑运算、||、&&

// 1、JavaScript中返回false的值
console.log(7 == undefined); // false
console.log(7 == null); // false
console.log(7 == NaN); // false
console.log(7 == ''); // false
console.log(7 == 0); // false
console.log(7 == false); // false
console.log(7 == []); // false
console.log(7 == {}); // false

// 2、逻辑运算
// 2.1、&&(且)
console.log(1 && 2); // 2 如果都是真,返回最后的值
console.log(1 && 2 && undefined && 10); // undefined 如果遇到假,直接返回值

// 2.2、||(或)
console.log(1 || 2); // 1 如果遇到真,直接返回值
console.log(undefined || NaN || null); // null 如果遇到假,一直判断到最后,最终返回最后一个

// 2.3、!(非)
console.log(!1); // false
console.log(!!1); // true
console.log(7 != 7); // false
console.log(7 != !7); // true

JavaScript之函数的实际参数(实参)和形式参数(形参)、arguments(实参)、(a, b, c, d实参)、字面量定义具名函数

let test = function testName(a, b, c) {
	console.log('实参个数:', arguments.length);
	// 实参个数: 2
	console.log('函数形参个数:', testName.length);
	// 函数形参个数: 3

	// 基本类型数据-----------------------------------------
	// 通过形参方式修改
	a = 2;
	console.log('实参 a:', arguments[0]);
	// 实参 a: 1
	console.log('形参 a:', a);
	// 形参 a: 2

	// 通过实参方式修改
	arguments[0] = 3;
	console.log('实参 a:', arguments[0]);
	// 实参 a: 3
	console.log('形参 a:', a);
	// 形参 a: 2

	// 基本类型数据的实参与形参变化互不影响

	// 引用类型数据-----------------------------------------
	// 通过形参方式修改
	b.a = 6;
	console.log('实参 b:', arguments[1].a);
	// 实参 b: 6
	console.log('形参 b:', b.a);
	// 形参 b: 6

	// 通过实参方式修改
	arguments[1].a = 7;
	console.log('实参 b:', arguments[1].a);
	// 实参 b: 7
	console.log('形参 b:', b.a);
	// 形参 b: 7

	// 引用类型数据的实参与形参变化相互影响,
	// 因为使用的是引用地址关系

	// 实参与形参的映射-------------------------------------
	c = 9;
	console.log('实参 c:', arguments[2]);
	// 实参 c: undefined
	console.log('形参 c:', c);
	// 形参 c: 9

	arguments[2] = 5;
	console.log('实参 c:', arguments[2]);
	// 实参 c: 5
	console.log('形参 c:', c);
	// 形参 c: 9

	// 实参个数少于形参个数时
	// 多出来的形参个数与实参个数不能形成映射
	// 所以实参个数为undefined

	return { a, b, c };
}

console.log(test(1, { a: 1 }));
// {a: 2, b: {…}, c: 9}

JavaScript之使用字面量定义具名函数,且实现递归

// 定义退出递归的条件,
// 否则进入无限循环
let a = 0;

let test = function testName() {
	a += 1;
	if (a == 3) return;
	testName();
}

console.log(test(0));
// undefined
testName();
// ReferenceError: testName is not defined

// 使用字面量定义具名函数,
// 在函数外部无法通过名字调用函数,
// 只能通过变量调用函数

JavaScript之形参默认值、实参、undefined占位

function test(a = 1, b = 2) {
	// ES5不支持形参默认值的解决方法
	// 曲线救国
	a = arguments[0] || 1;
	b = arguments[1] || 2;
	console.log(a);
	console.log(b);
}

test(undefined, 3);
// 此处是因为只需要传第二参数
// 因为形参与实参是一一对应,
// 所以需要使用undefined来占位

JavaScript校验特殊字符、indexOf、substr

function specialCharacters(str) {
	if (!str) return true;

	const specialKey = "[`~!#$^&*()=|{}': ; '\\[\\].<>/?~!#¥……&*()——|{}【】‘;:”“'。,、?‘']";
	for (var i = 0; i < str.length; i++) if (specialKey.indexOf(str.substr(i, 1)) != -1) return false;

	return true;
}

specialCharacters('哈.哈') ? console.log('校验通过') : console.error('存在特殊字符!');

JavaScript之合并两个对象、Object、assign

function mergeTwoObjects(params1, params2) {
    if (!params1 || !params2) return '参数不能为空!';
    return Object.assign(params1, params2);
}

console.log(mergeTwoObjects({a: 1, b: 3}, {c: 6, b: 5}));
// {a: 1, b: 5, c: 6}
console.log(mergeTwoObjects({a: 1, b: 3}, [6, 5]));
// {0: 6, 1: 5, a: 1, b: 3}
console.log(mergeTwoObjects([1, 3], {a: 6,b: 5}));
// [1, 3, a: 6, b: 5]
console.log(mergeTwoObjects([1, 3], [6, 5]));
// [6, 5]

JavaScript之中划线、下划线隔开的变量转为小驼峰、横杠、replace、substr、toUpperCase、正则、RegExp

function camelCase(params) {
	// 正则匹配到两个字符
	// 使用substr获取最后一个字符
	// 再使用toUpperCase转为大写
    return params.replace(/[_-][a-zA-z]/g, str => str.substr(-1).toUpperCase());
}

console.log(camelCase('user_name'));
// userName
console.log(camelCase('user-number'));
// userNumber
console.log(camelCase('user_Password'));
// userPassword
console.log(camelCase('user-Age'));
// userAge

深克隆(clone)–02

1、数据

let oldData = {
  name: '西施',
  age: 26,
  height: '163.30',
  score: null,
  friends: ['李白', '杜牧', '李商隐'],
  oldObj: {
    city: '广州',
    area: '越秀区',
    street: '明信路',
  },
};

2、克隆函数

function deepClone(data) {
  	// 第一步:判断传进来的数据是否为空 或者 是基本类型的值
  	if (data === null || typeof data !== 'object') return data;
  	// 第二步:判断数据是否是数组
  	// 如果是数组创建一个空数组
	// 否则创建一个空对象
  	let cloneData = Array.isArray(data) ? [] : {};
	// 第三步:使用 for in 循环遍历数据
  	for (let key in data) {
  		// 判断结束递归
    	if (data.hasOwnProperty(key)) {
      		// Object.prototype.hasOwnProperty() 方法会返回一个布尔值,
      		// 指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键) 
      		// deepClone(data[key]); 使用了函数递归
      		cloneData[key] = deepClone(data[key]);
    	}
	}
  	return cloneData;
};

3、验证

// 克隆后的数据
let cloneData = deepClone(oldData);

// 如果是 false 说明克隆成功
console.log(oldData === cloneData); // false

// 修改克隆后的第一层 age 的值
cloneData.age = 32;

// 修改克隆后第二层 数组的值
cloneData.friends[2] = '杜甫';

// 修改克隆后的二层 city 的值
cloneData.oldObj.city = '武汉';

// 深克隆,第一层两个值的地址已经完全不一样了
console.log('oldObject:', oldData.age); // 26
console.log('newObject:', cloneData.age); // 32

// 深克隆,第二层 数组 两个值的地址已经完全不一样了
console.log('oldObject:', oldData.friends[2]); // 李商隐
console.log('newObject:', cloneData.friends[2]); // 杜甫

// 深克隆,第二层 对象 两个值的地址已经完全不一样了
console.log('oldObject:', oldData.oldObj.city); // 广州
console.log('newObject:', cloneData.oldObj.city); // 武汉

函数柯理化-01

1、简单示例 add 函数

// Currying 前
function add(x, y) {
    return x + y;
};

// Currying 后
function curryingAdd(x) {
    return function (y) {
        return x + y;
    };
};

add(1, 2); // 3
curryingAdd(1)(2); // 3

实际上就是把 add 函数的 x, y 两个参数变成了先用一个函数接收 x 然后返回一个函数去处理 y 参数。就是只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。


2、参数复用

// Currying 前
function check(reg, txt) {
    return reg.test(txt);
};

check(/\d+/g, 'test'); // false
check(/[a-z]+/g, 'test'); // true

// Currying 后
function curryingCheck(reg) {
    return function(txt) {
        return reg.test(txt);
    };
};

let hasNumber = curryingCheck(/\d+/g);
let hasLetter = curryingCheck(/[a-z]+/g);

hasNumber('test1'); // true
hasNumber('testtest'); // false
hasLetter('21212'); // false

示例是一个正则的校验,正常来说直接调用 check 函数就可以,但是如果有很多地方都要校验是否有数字,其实就是需要将第一个参数 reg 进行复用,这样别的地方就能够直接调用 hasNumber,hasLetter 等函数,让参数能够复用,调用起来也更方便。


3、提前确认
3.1、示例壹

let on = function(element, event, handler) {
    if (document.addEventListener) {
        if (element && event && handler) {
            element.addEventListener(event, handler, false);
        };
    } else {
        if (element && event && handler) {
            element.attachEvent('on' + event, handler);
        };
    };
};

3.2、示例贰

let on = (function() {
    if (document.addEventListener) {
        return function(element, event, handler) {
            if (element && event && handler) {
                element.addEventListener(event, handler, false);
            };
        };
    } else {
        return function(element, event, handler) {
            if (element && event && handler) {
                element.attachEvent('on' + event, handler);
            };
        };
    };
})();
// ()(); => ~function() {.. ..}();

3.3、示例叁

// 上面就是把 isSupport 这个参数给先确定下来了
let on = function(isSupport, element, event, handler) {
    isSupport = isSupport || document.addEventListener;
    if (isSupport) {
        return element.addEventListener(event, handler, false);
    } else {
        return element.attachEvent('on' + event, handler);
    };
};

在做项目的过程中,封装一些 dom 操作可以说再常见不过,上面第一种写法也是比较常见,但是看看第二种写法,它相对于第一种写法就是自执行然后返回一个新的函数,这样其实就是提前确定了会走哪一个方法,避免每次都进行判断。


4、延迟运行

Function.prototype.bind = function (context) {
    let that = this;
    let args = Array.prototype.slice.call(arguments, 1);
 
    return function() {
        return that.apply(context, args);
    };
};

js 中经常使用的 bind,实现的机制就是 Currying。


5、通用的封装方法
5.1、初步封装

let currying = function(fn) {
    // args 获取第一个方法内的全部参数
    let args = Array.prototype.slice.call(arguments, 1);
    return function() {
        // 将后面方法里的全部参数和 args 进行合并
        var newArgs = args.concat(Array.prototype.slice.call(arguments));
        // 把合并后的参数通过 apply 作为 fn 的参数并执行
        return fn.apply(this, newArgs);
    };
};

通过闭包把初步参数给保存下来,然后通过获取剩下的 arguments 进行拼接,最后执行需要 currying 的函数。但是,有缺陷,这样返回的只能多扩展一个参数,currying(a)(b)© 这样的话,就不支持多参数调用了。


5.2、递归封装

function progressCurrying(fn, args) {
    let that = this;
    let len = fn.length;
    let args = args || [];
    
    return function() {
        let _args = Array.prototype.slice.call(arguments);
        Array.prototype.push.apply(args, _args);
        // 如果参数个数小于最初的fn.length,则递归调用,继续收集参数
        if (_args.length < len) {
            return progressCurrying.call(that, fn, _args);
        }
        // 参数收集完毕,则执行fn
        return fn.apply(this, _args);
    };
};

其实是在初步的基础上,加上了递归的调用,只要参数个数小于最初的fn.length,就会继续执行递归。


6、经典面试题

function add() {
    // 第一次执行时,定义一个数组专门用来存储所有的参数
    let _args = Array.prototype.slice.call(arguments);
    
    // 在内部声明一个函数,
    // 利用闭包的特性保存_args并收集所有的参数值
    let _adder = function() {
        _args.push(...arguments);
        return _adder;
    };
    
    // 利用toString隐式转换的特性,
    // 当最后执行时隐式转换,并计算最终的值返回
    _adder.toString = function () {
        return _args.reduce(function (a, b) {
            return a + b;
        });
    };
    return _adder;
};

add(1)(2)(3);               // 6
add(1, 2, 3)(4);            // 10
add(1)(2)(3)(4)(5);         // 15
add(2, 6)(1);               // 9

柯理化函数之参数复用

1、正常函数封装

function check(reg, txt) {
    return reg.test(txt);
}

check(/\d+/g, 'test');       // false
check(/[a-z]+/g, 'test');    // true

缺点:每次调用都要多传一个参数


2、Currying后

function curryingCheck(reg) {
	// 使用闭包机制保存 reg 值
	// 形成多个执行上下文
	// 通过外部调用的引用,使其作用域无法释放
    return (txt) => reg.test(txt);
}

// 使用变量接收外部函数返回的内部函数
// 在调用外部函数的时候传递参数,并且参数会保存在闭包中
// 当调用内部函数时会通过作用域链机制找到外层函数保存的变量
let hasNumber = curryingCheck(/\d+/g);
let hasLetter = curryingCheck(/[a-z]+/g);

// 此时调用的是返回的内部函数
hasNumber('test1');      // true
hasNumber('testtest');   // false
hasLetter('21212');      // false

示例是一个正则的校验,正常来说直接调用 check 函数就可以,但是如果有很多地方都要校验是否有数字,其实就是需要将第一个参数 reg 进行复用,这样别的地方就能够直接调用 hasNumber,hasLetter 等函数,让参数能够复用,调用起来也更方便。


面向对象的三大特性

1、封装
1.1、如果一个对象不需要反复创建

var object = {
	Uname: '张三',
	Uage: 56
};

1.2、如果一个对象需要反复创建,使用构造函数即可

// 第一步:定义构造函数
function Student(name, age) { 
	this.name = name; 
	this.age = age; 
	// 不能将方法定义在构造函数中,
	// 因为会被多次创建,每创建一个实例就会创建一次方法。
};

// 第二步:用 new 调用构造函数
var newFun = new Student();

2、继承
所有子对象共有的方法,应该添加到构造函数的原型对象中,子对象调用方法时,先在子对象本地查找。如果本地对象没有找到,才延原型链向父级对象查找,直到找到为止。


3、多态
如果从父对象继承来的方法不好用,可在对象本地定义同名方法,覆盖父对象中的方法(重写)。

强调:原型对象中,方法 (函数) 里面的 this 指向由调用该方法的 点(.) 前的某个子对象 (实例) 来决定。


class继承


第一步

1、先创建一个额外的新的父类型,类名能够概括两种子类型的特征。
2、两种子类型间相同的属性结构,统一定义到父类型的构造函数中。
3、两种子类型间相同的方法,统一定义到父类型的原型对象中。

class Enemy {
	constructor(x, y) {
		this.x = x;
		this.y = y;
	};
	
	fly() {
		console.log(`飞行到 x: ${this.x},y: ${this.y}的位置。`);
	};
};

第二步

1、让子类型继承父类型: class 子类型 extends 父类型 { }。其实就是用 extends 关键词代替 Object.setPrototypeOf()。
2、在子类型构造函数中,用 super 调用父类型的构造函数,执行父类型构造函数中的语句,为子对象添加公共的属性。super 是子类型中,专门指向父类型中构造函数的关键词。

// 子类 Plane 通过 extends 继承父类 Enemy
// extends: 扩展
class Plane extends Enemy {
	constructor(x, y, score) {
		// this.x = x;
		// this.y = y;
		// 子类通过 super 可以拿到父类中的属性和方法
		super(x, y);
		this.score = score;
 	};
 	
	// fly() {
		//     console.log('飞行');
	// };
	getScore() {
		console.log(`击落一架敌机,得${this.score}分。`);
	};
};

var plane = new Plane(100, 50, 5);
console.log(plane); // {x: 100, y: 50, score: 5}
plane.fly(); // 飞行到 x: 100,y: 50的位置。
plane.getScore(); // 击落一架敌机,得5分。

// 子类 Parachute 通过 extends 继承父类 Enemy
// extends: 扩展
class Parachute extends Enemy {
	constructor(x, y, award) {
		// this.x = x;
		// this.y = y;
		// 子类通过 super 可以拿到父类中的属性和方法
		super(x, y);
		this.award = award;
	};
	
	// fly() {
		//     console.log('飞行');
	// };
	getAward() {
		console.log(`击落一把降落伞,得${this.award}分。`);
	};
};

var parachute = new Parachute(100, 100, 30);
console.log(parachute); // {x: 100, y: 100, award: 30}
parachute.fly(); // 飞行到 x: 100,y: 100的位置。
parachute.getAward(); // 击落一把降落伞,得30分。

闭包–节流函数–笔试-10

1、定义

节流函数的作用是在限定的时间内函数只执行一次。
1、按钮提交(可以避免重复提交,当然不只这种方法,将按钮设置为不可用也可以)。
2、scroll、mousehover、mousemove 等触发频率高的时候。
主要的原理就是在闭包内设置一个标记,在限定的时间内这个 flag 设置为 true,函数再次点击则让执行,setTimeout 函数执行以后将 flag 设置为 flase,就可以继续执行 。


2、html

DOCTYPE html>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=GBK">
	head>
	<body>
		<div class="box" id="div_box" >
			<button onclick="fn1()">testbutton>
		div>
	body>
html>

3、JavaScript

function throttle(fn, delay) {
	var flag = false;
	var timer = null;
	
	return function() {
		// 将参数转成数组
		var args = [].slice.call(arguments, 0); 
		var context = this;
		
		// 如果在限定的时间内 flag 是 true 则直接返回,不让执行
		if(flag) return false;
		// 函数正在控制中
		flag = true; 
		// 执行函数
		fn.apply(context, args);
		// 清除定时器
		clearTimeout(timer); 
		timer = setTimeout(function() {
			// 延时时间过了以后,放开函数控制
			flag = false; 
		}, delay);	
	}
}

function fn() {
	console.log(123);
}

// 绑定节流函数
var fn1 = throttle(fn, 2000);  

构造函数继承-1

1、父函数

function parentClass() {
	this.parentVal = '父类的基本类型值。';
	this.parentFun = function (data) {
		console.log(this.parentVal);
		// 父类的基本类型值。

		console.log(data);
		// 调用父类方法出入的值。
	
		return '父类方法返回的值。';
	};
};

2、父函数原型上的方法

parentClass.prototype.parentProtyFun = function (data) {
	console.log(this.parentVal);
	// 父类的基本类型值。

	console.log(data);
	// 调用原型方法传入的值。

	return '原型方法返回的值。';
};

3、子函数

function student(name) {
	console.log(name); // new 时传入的值。
	parentClass.apply(this, arguments);
	this.name = name;
};

4、实现继承

// 这种方式只能继承父类构造函数中的属性和方法,
// 对于原型对象上的方法无法继承。

// 构造函数继承是利用 call 或者 apply 方法,
// 将父类构造函数的 this 绑定到子类构造函数的 this 上即可。
let example = new student('new 时传入的值。');

console.log(example);
// student {parentVal: "父类的基本类型值。", name: "new 时传入的值。", parentFun: ƒ}

console.log(example.parentVal);
// 父类的基本类型值。

console.log(example.parentFun('调用父类方法出入的值。'));
// 父类方法返回的值。

console.log(parentClass.prototype);
// {parentProtyFun: ƒ, constructor: ƒ}

console.log(example.parentProtyFun('原型方法'));
// example.parentProtyFun is not a function

5、参考的原文链接
知乎-原文


JavaScript数据类型有哪些?如何存储值?

1、数据类型

JavaScript共有8种数据类型。
7种基本数据类型:NullUndefinedBooleanNumberStringSymbol(ES6新增,表示独一无二的值)和BigInt(ES10新增)。
1种引用数据类型:Object。Object里面包含ObjectArrayFunctionDateRegExp等。
总结:JavaScript不支持任何创建自定义类型的机制,而所有值最终都将是上述8种数据类型之一。


2、存储方式
2.1、原始数据类型

直接存储在栈(stack)中,占据空间小且大小固定,属于被频繁使用的数据,所以放入栈中存储。


2.2、引用数据类型

同时存储在栈(stack)和堆(heap)中,占据空间大,且大小不固定。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。


null和undefined的区别是什么?

1、null
1.1、用来初始化一个变量,这个变量可能赋值为一个对象。

var a = null;
console.log(a); 
// null
let b = { a: null };
console.log(b); 
// {a: null}
const c = [null];
console.log(c); 
// [null]

1.2、用来和一个已经初始化的变量比较,这个变量可以是对象,也可以不是对象。

var a = 10;
console.log(a == null); 
// false
var b = null;
console.log(b == null); 
// false
var c = {};
console.log(c == null); 
// false
var d = [];
console.log(d == null); 
// false

1.3、当函数的参数期望是对象时,用作参数的传入。

function fn(a) {
	console.log(a); 
	// null
	console.log(typeof a); 
	// object
};
fn(null);

1.4、当函数的返回值期望是对象时,用作返回值传出。

function fn(a) {
	console.log(a); 
	// 1
	return null;
};
console.log(fn(1)); 
// null
console.log(typeof fn(1)); 
// object

2、undefined
2.1、声明一个变量,但是没有赋值。

var vlcs;
console.log(vlcs); 
// undefined

2.2、访问对象上不存在的属性或者未定义的变量。

console.log(Object.foo); 
// undefined
console.log(typeof demo); 
// undefined

2.3、函数定义了形参,但没有传递实参。

// 定义形参 a
function fn(a) {
	console.log(a); 
	// undefined
};
//未传递实参
fn(); 

2.4、使用void对表达式求值。

void 0; 
// undefined
void false; 
// undefined
void []; 
// undefined
void null; 
// undefined
void function fn() {}; 
// undefined

3、总结

3.1、undefined表示一个变量最原始的自然状态值。
3.2、null表示一个变量被人为的设置为空对象, 而不是原始状态。
3.3、在实际使用过程中,为了保证变量所代表的语义,不要对一个变量赋值为undefined,当需要释放一个对象时,直接赋值为null即可。


图片懒加载

1、css

.img_box {
	margin-top: 30px;
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: center;
	border: 1px solid red;
}
        
.img_box>img {
	width: 450px;
	height: 500px;
	margin: 5px 0;
}

2、html

<div class="img_box">
	
	
	
	<img src="/img/loading02.gif" data-src="/img/01.jpg" alt="">
	<img src="/img/loading02.gif" data-src="/img/02.jpg" alt="">
	<img src="/img/loading02.gif" data-src="/img/03.jpg" alt="">
	<img src="/img/loading02.gif" data-src="/img/04.jpg" alt="">
	<img src="/img/loading02.gif" data-src="/img/05.jpg" alt="">
	<img src="/img/loading02.gif" data-src="/img/06.jpg" alt="">
	<img src="/img/loading02.gif" data-src="/img/07.jpg" alt="">
	<img src="/img/loading02.gif" data-src="/img/08.jpg" alt="">
	<img src="/img/loading02.gif" data-src="/img/09.jpg" alt="">
div>

3、JavaScript

// onload 是等所有的资源文件加载
// 完毕以后再绑定事件
window.onload = function() {
	// 1、获取一面图片标签元素
	// 获取图片列表,
	// 即 img 标签列表
	let imgs = document.querySelectorAll('img');

	// 2、获取到浏览器顶部的距离
	function getTop(e) {
		return e.offsetTop;
	}

	// 3、懒加载实现
	function lazyload(dataImg) {
		// 3.1、获取可视区域高度
		let innerHeight = window.innerHeight;
		// 3.2、获取滚动区域高度
		let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;

		for (let i = 0; i < dataImg.length; i++) {
			// 3.3、如果图片距离顶部的距离大于
			// 可视区域和滚动区域之和时触发懒加载
			if ((innerHeight + scrollTop) > getTop(dataImg[i])) {
				// 3.4、真实情况是页面开始有 1 秒空白,
				// 所以使用 setTimeout 定时 1s
				(function(i) {
					setTimeout(function() {
						// 3.5、不加立即执行函数 i 会等于 9
						// 隐形加载图片或其他资源,
						// 创建一个临时图片,
						// 这个图片在内存中不会到页面上去。
						// 实现隐形加载
						let temp = new Image();
						// console.log('new:', temp);

						// 3.6、只会请求一次
						temp.src = dataImg[i].getAttribute('data-src');
						// console.log('src:', temp);

						// 3.7、onload 判断图片加载完毕,
						// 真实图片加载完毕,
						// 再赋值给 dom 节点
						temp.onload = function() {
							// 3.8、获取自定义属性 data-src,
							// 用真图片替换假图片
							dataImg[i].src = dataImg[i].getAttribute('data-src');
							// console.log('dataImg:', dataImg[i].src);
						};
					}, 1000);
				})(i);
			}
		}
	}

	// 4、调用懒加载函数,
	// 传递图片标签元素
	lazyload(imgs);

	// 5、滚动监听
	window.onscroll = function() {
		// 调用懒加载函数
		lazyload(imgs);
	};
}

JavaScript之对象 (object) 解构

1、定义一个普通对象

let objectData = {
	Uname: '李清照',
	age: '20',
	interest: {
		writeCode: '写代码',
		readBook: '看书'
	},
	introduce: function() {
		console.log(`我叫${Uname},今年${age}。喜欢${writeCode}${readBook}`);
	}
};

2、解构重命名

// Uname: newUname 的意思是: 取到 Uname 的属性值,
// 冒号后面的变量名没有花括号,
// 如果有花括号则可以继续解构。
// 意思是把取到的值交给冒号后面的变量名,
// 相当于把 Uname 变量重命名为 newUname
let {
	Uname: newUname, 
	age: newAge, 
	interest: {
		writeCode: newWriteCode,
		readBook: newReadBook
	},
	introduce: newIntroduce
} = objectData;

console.log(newUname); // 李清照
console.log(newAge); // 20
console.log(newWriteCode); // 写代码
console.log(newReadBook); // 看书
newIntroduce(); // 我叫李清照,今年20。喜欢写代码和看书。

3、直接使用,不需重新命名

let {
	Uname, 
	age, 
	interest: {
		writeCode,
		readBook
	},
	introduce
} = objectData;

console.log(Uname); // 李清照
console.log(age); // 20
console.log(writeCode); // 写代码
console.log(readBook); // 看书
introduce(); // 我叫李清照,今年20。喜欢写代码和看书。

检测一个对象是不是数组

1、基本数据类型

var number = 100;
var string = 'asdfghjkl';
var boolean = true;
var nu = null;
var un = undefined;

2、引用数据类型

var fun = function() {};
var object = {};
var array = [3, 6, 9];
var date = new Date();

3、typeof()或者typeof,因为typeof有局限性,所以不能用来做数组类型的检测

// typeof()的局限性
console.log(typeof(number)); 
// number
console.log(typeof string); 
// string
console.log(typeof(boolean)); 
// boolean

// 因为null的意思是指向一个空地址
// 所以更像一个没有意义的空对象
console.log(typeof(nu)); 
// object
console.log(typeof un); 
// undefined

// typeof可以检测函数
console.log(typeof(fun)); 
// function

// typeof不能检测对象的具体类型
// 把除了Function对象以外的引用类型对象都检测为object
console.log(typeof object); 
// object
console.log(typeof array); 
// object
console.log(typeof(date)); 
// object

4、proto

console.log(object.__proto__ == Array.prototype); 
// false
console.log(array.__proto__ == Array.prototype); 
// true
console.log(date.__proto__ == Array.prototype); 
// false

5、Object.getPrototypeOf([value])

// __proto__可能被浏览器禁用,所以有等效的函数检测。
console.log(Object.getPrototypeOf(object) == Array.prototype); 
// false
console.log(Object.getPrototypeOf(array) == Array.prototype); 
// true
console.log(Object.getPrototypeOf(date) == Array.prototype); 
// false

6、Array.prototype.isPrototypeOf([value])

console.log(Array.prototype.isPrototypeOf(object)); 
// false
console.log(Array.prototype.isPrototypeOf(array)); 
// true
console.log(Array.prototype.isPrototypeOf(date)); 
// false

7、使用构造函数constructor进行检测,即使用父级原型对象中的constructor属性

console.log(object.constructor == Array); 
// false
console.log(array.constructor == Array); 
// true
console.log(date.constructor == Array); 
// false

8、[value] instanceof Array

console.log(object instanceof Array); 
// false
console.log(array instanceof Array); 
// true
console.log(date instanceof Array); 
// false

9、万能方法,不存在兼容性:[value].prototype.toString().call()

console.log(Object.prototype.toString.call(object)); 
// [object Object]
console.log(Object.prototype.toString.call(array)); 
// [object Array]
console.log(Object.prototype.toString.call(date)); 
// [object Date]

10、isArray():ES5新增,存在兼容性问题

console.log(Array.isArray(object)); 
// => false
console.log(Array.isArray(array)); 
// => true
console.log(Array.isArray(date)); 
// => false

11、以上方法需要了解面向对象、原型prototype和原型链

web前端之零碎知识点、字符串、数组、基础知识_第1张图片


JavaScript之new操作符

1、new操作符做的事情-01

1. 创建了一个全新的对象。
2. 将对象链接到这个函数的prototype对象上。
3. 执行构造函数,并将this绑定到新创建的对象上。
4. 判断构造函数执行返回的结果是否是引用数据类型,若是则返回构造函数执行的结果,否则返回创建的对象。


2、new操作符做的事情-02

1. 创建一个全新的对象 (无原型的Object.create(null)) 。目的是保存new出来的实例的所有属性。
2. 将构造函数的原型赋值给新创建的对象的原型。目的是将构造函数原型上的属性继承下来。
3. 调用构造函数,并将this指向新建的对象。目的是让构造函数内的属性全部转交到该对象上,使得this指向改变,方法有三 : apply、call、bind 。
4. 判断构造函数调用的方式,如果是new的调用方式,则返回经过加工后的新对象,如果是普通调用方式,则直接返回构造函数调用时的返回值。


3、代码

function myNew(Constructor, ...args) {
	// 判断Constructor参数是否是函数
	if (typeof Constructor !== 'function') return 'Constructor.apply is not a function';

	// 1、创建了一个全新的对象。
	let newObject = {};

	// 2、将对象链接到这个函数的prototype对象上。
	newObject.__proto__ = Constructor.prototype;

	// 此处是把 1 / 2 步结合到一起
	// const newObject = Object.create(Constructor.prototype);

	// 3、执行构造函数,
	// 并将this绑定到新创建的对象上。
	let result = Constructor.apply(newObject, args);

	// 4. 判断构造函数执行返回的结果是否是引用数据类型,
	// 若是则返回构造函数执行的结果,
	// 否则返回创建的对象。
	if ((result !== null && typeof result === 'object') || (typeof result === 'function')) return result;
	return newObject;
};

// 需要被new的函数
function NewTest(args) {
	this.dataValue = args;
	return this;
};

// 定义参数
let dataObj = {
	sname: '杨万里',
	number: 3,
	web: 'vue'
};
let dataArray = [5, 'uniApp', '范仲淹'];

// 执行myNew函数
let test = myNew(NewTest, 1, 'JavaScript', '辛弃疾', dataObj, dataArray);
console.log(test); // NewTest {dataValue: Array(5)}

4、new的原理是什么

new操作符做的事情。


5、通过new的方式创建对象和通过字面量创建对象有什么区别

对象都是通过new产生。function Foo() {},function是语法糖,内部等同于new Function() 。
let object = { number: 1 } ,使用字面量创建对象,内部也是使用了new Object() 。
对于创建一个对象来说,更推荐使用字面量的方式创建对象。因为使用new Object()的方式创建对象,需要通过作用域链一层层找到Object ,如果使用字面量的方式就没有这个问题。


原型实例继承-2

1、父函数

function parentClass() {
	this.parentVal = '父类的基本类型值。';
	
	this.parentFun = function (data) {
		console.log(this.parentVal);
		// 父类的基本类型值。

		console.log(data);
		// 调用父类方法出入的值。

		return '父类方法返回的值。';
	};
};

2、父函数原型上的方法

parentClass.prototype.parentProtyFun = function (data) {
	console.log(this.parentVal);
	// 父类的基本类型值。

	console.log(data);
	// 调用原型方法传入的值。

	return '原型方法返回的值。';
};

3、子函数

function student(name) {
	this.name = name;
	console.log(name); // new 时传入的值。
	parentClass.apply(this, arguments);
};

4、实现继承

// 这是比较常用的一种实现继承的方式。
// 1、将 student 的 prototype 对象指向 parentClass 的一个实例。
// 此操作完全删除了 student.prototype 对象原本的内容,
// 然后赋予给它一个新的值。
student.prototype = new parentClass();

// 2、任何一个构造函数都有一个 prototype 对象,
// 这个 prototype 对象都有一个 constructor 属性指向自身的构造函数。
// 2.1、因为第一行对 prototype 对象进行了重新赋值,
// 所以 prototype 对象的 constructor 属性也发生了改变,
// 变成指向 parentClass ,
// 所以必须手动将 student.prototype.constructor 指回 student 。
// 2.2、如果没有(student.prototype.construct = student) 这行代码,
// 则 student.prototype.constructor == parentClass 和
// example.constructor == parentClass 的结果返回 false 。
// 2.3、这里比较好理解,因为 example 是 student 的实例化对象,
// 它的 constructor 属性默认继承自 parentClass.prototype ,
// 而 parentClass.prototype 的 constructor 属性继承自 parentClass.prototype ,
// 最后找到 parentClass.prototype.constructor 指向 parentClass 。
// 显然如果没有这句话,将会导致继承链的错乱。
// 注意:在编程时,如果对 prototype 对象进行重新赋值后,
// func.prototype = { sname: '李白' };
// 记得手动奖 prototype 的 constructor 属性智慧原来的构造函数。
// func.prototype.constructor = func;
student.prototype.construct = student;

let example = new student('new 时传入的值。');

console.log(example);
// {parentVal: "父类的基本类型值。", name: "new 时传入的值。", parentFun: ƒ}

console.log(example.parentVal);
// 父类的基本类型值。

console.log(example.parentFun('调用父类方法出入的值。'));
// 父类方法返回的值。

console.log(example.parentProtyFun('调用原型方法传入的值。'));
// 原型方法返回的值。

5、参考的原文链接
知乎-原文


原型直接继承-3

1、父函数

function parentClass() {
	this.parentVal = '父类的基本类型值。';
	this.parentFun = function (data) {
		console.log(this.parentVal);
		// 父类的基本类型值。

		console.log(data);
		// 调用父类方法出入的值。

		return '父类方法返回的值。';
	};
};

2、父函数原型上的方法

parentClass.prototype.parentProtyFun = function (data) {
	console.log(this.parentVal);
	// 父类的基本类型值。

	console.log(data);
	// 调用原型方法传入的值。

	return '原型方法返回的值。';
};

3、子函数

function student(name) {
	this.name = name;
	console.log(name); // new 时传入的值。
	parentClass.apply(this, arguments);
};

4、实现继承

// 原型直接继承是通过直接将子类构造函数的原型对象,
// 直接赋值为父类构造函数的原型对象,
// 从而达到继承的目的。
student.prototype = parentClass.prototype;
student.prototype.constructor = student;

// new 实例
let example = new student('new 时传入的值。');

console.log(example);
// student {parentVal: "父类的基本类型值。", name: "new 时传入的值。", parentFun: ƒ}

console.log(example.parentVal);
// 父类的基本类型值。

console.log(example.parentFun('调用父类方法出入的值。'));
// 父类方法返回的值。

console.log(example.parentProtyFun('调用原型方法传入的值。'));
// 原型方法返回的值。

5、参考的原文链接
知乎-原文


ES6的Class继承、super、extends、constructor-4

1、父类

class parent {
	constructor(name) {
		this.sname = name;
	};

	parentFunc(data) {
		console.log(this.sname);
		// new 时传入的值。

		console.log(data);
		// 调用父类方法传入的值。
		
		return '父类方法的返回值。';
	};
};

2、子类(直接实现继承)

class children extends parent {
	constructor(name) {
		super(name);
		this.sname = name;
	};
	
	childrenFunc(data) {
		console.log(this.sname);
		// new 时传入的值。

		console.log(data);
		// 调用子类方法传入的值。

		return '子类方法的返回值。';
	};
};

3、使用

// new 实例
let example = new children('new 时传入的值。');

console.log(example);
// new 时传入的值。

console.log(example.sname);
// new 时传入的值。

console.log(example.parentFunc('调用父类方法传入的值。'));
// 父类方法的返回值。

console.log(example.childrenFunc('调用子类方法传入的值。'));
// 子类方法的返回值。

4、参考的原文链接
知乎-原文


作用域-作用域链

1、作用域
1.1、定义

作用域是指程序中定义变量的区域,它决定了当前执行代码对变量的访问权限。


1.2、全局作用域

一般指的是widow。


1.3、函数作用域

定义在函数中的变量就在函数作用域中。并且函数在每次调用时都有一个不同的作用域。这意味着同名变量可以用在不同的函数中。因为这些变量绑定在不同的函数中,拥有不同作用域,彼此之间不能访问。


1.4、块级作用域

???


1.5、词法作用域

词法作用域是指一个变量的可见性,及其文本表述的模拟值。


1.6、动态作用域

???


2、作用域链
定义

查找变量的时候,先从当前上下文的变量对象中查找,如果没有找到,就向父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象(全局对象),如果全局上下文对象中也没有找到变量,则返回undefined。这样由多个执行上下文的变量对象构成的链表就是作用域链。


3、参考文章
谈谈JavaScript的作用域


原型与原型链-总结

1、概念

在JavaScript中,对象都有__proto__属性(隐式原型),指向构造该对象的构造函数的原型,而函数Function比较特殊,它除了和其他对象一样有__proto__属性外,还有自己特有的属性prototype称之为原型对象,原型对象有一个constructor属性,该属性指回该函数本身。


2、关键字介绍

1、每个函数都会有prototype属性,普通对象没有prototype属性。prototype是构造函数的原型对象。
2、每个对象都有双下划线__proto__属性,因为函数也是对象,所以函数也有双下划线__proto__属性。它指向构造函数的原型对象。
3、constructor是原型对象上的一个指向构造函数的属性。


闭包的定义

定义-01

闭包是指有权访问另一个函数作用域中的变量的函数。


定义-02

闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。


定义-03

闭包可以让一个函数访问并操作其声明时的作用域中的变量和函数,即使声明时的作用域消失了,也可以调用。


定义-04

闭包是一个定义在其它函数(父函数)里面的函数,它拥有对父函数里面变量的访问权。闭包有三个作用域的访问权。自身的作用域、父作用域和全局作用域。


原型实例继承和原型直接继承的区别?

1、原型实例继承

student.prototype = new parentClass();
student.prototype.construct = student;

2、原型直接继承

student.prototype = parentClass.prototype;
student.prototype.constructor = student;

渲染(绑定)数据的时候嵌套两层(需要点(.)两次)的时候报错的解决方案

1、错误写法

<div>{{item.obj.name}}div>

2、正确写法

<div>{{(item.obj || {}).name}}div>

事件队列-笔试题

示例-1

setTimeout(() => {
	console.log(1);
}, 20);

console.log(2);

setTimeout(() => {
	console.log(3);
}, 10);

console.log(4);

// console.time('time');
for (let i = 0; i < 90000000; i++) {
	// 900000 5 个 0:2.57421875 ms 左右
	// 9000000 6 个 0:12.625 ms 左右
	// 90000000 7 个 0:116.13525390625 ms 左右
	// 900000000 8 个 0:525.622314453125 ms 左右
	// 9000000000 9 个 0:9807.490966796875 ms 左右
	// 90000000000 10 个 0:循环不出来了
}

// console.timeEnd('time');
console.log(5);

setTimeout(() => {
	console.log(6);
}, 8);

console.log(7);

setTimeout(() => {
	console.log(8);
}, 15);

console.log(9);
// 2 4 5 7 9   3 1 6 8

循环的时间都超过了所有定时器的时间,在宏仁务中,定时器会按照从上到下的顺序执行,不再按照设置的时间长短来执行。


示例-2

console.log(1);

setTimeout(()=>{
	console.log(2);
}, 50);

console.log(3);

setTimeout(()=>{
	console.log(4);
	while(1 === 1) {};
	// 遇到死循环,
	// 所有代码执行都是在主栈中执行,
	// 主栈永远结束不了,
	// 后面啥都不干
}, 0);

console.log(5);
// 1 3 5   4

示例-3

console.log(1);

// 宏仁务
setTimeout(function () {
	console.log(2);
}, 0);

// 微任务
Promise.resolve().then(function () {
		console.log(3);
	}).then(function () {
		console.log(4);
});

console.log(5);
// 1 5   3 4 2

JavaScript两种解决跨域的方法

1、定义

1.1、浏览器同源(origin)策略:浏览器规定发送ajax请求时,只有相同域名的客户端和相同域名的服务端才能发送请求。
1.2、同源:.html在哪台服务器请求,数据也需要在哪台服务器请求。
1.3、报错:Access-Control-Allow-Origin不允许跨源头发送请求。
1.4、同源策略的本质:可以发送ajax请求,可以正常执行服务端的程序,也可以顺利返回正确的结果,但是,浏览器经过检查数据的来源,如果和当前网页的来源不一致,浏览器禁止使用此数据。


2、跨域的四种情况
2.1、域名不同

  • www.a.com
  • www.b.com

2.2、端口号不同

  • www.a.com:3000
  • www.a.com:5500

2.3、协议不同

  • http://www.a.com
  • https://www.a.com

2.4、域名和地址相互请求

  • http://localhost
  • http://127.0.0.1

3、可以跨域的标签

3.1、img
3.2、link
3.3、iframe
3.4、script


4、CORS(cross origin resource share)跨域

不要使用res.send(result); 这句话包含了下面三句话,属于下面三句的简写,重写这三句话。

res.writeHead(localhost:3000); // 写信封封面
res.write(); // 写信的内容
res.end(); // 发送
res.writeHead(200, {
	// 只针对某个地址实现跨域
	// 'Access-Control-Allow-Origin': 'http://127.0.0.1:5500',
	// 所有请求都允许跨域
	'Access-Control-Allow-Origin': '*',
	'Content-Type': 'application/json;charset=utf-8'
});
res.write(JSON.stringify([value]));
res.end();

5、JSONP(json with padding)跨域

这里的 padding 是填充。


方案一
客户端

<script src='http://127.0.0.1:8080'>script>

服务端

let weather = '晴 22-30℃';
let string = `document.write(${weather})`;
res.write(string);
res.end();
// 问题:服务端将客户端执行的程序固定了,
// 众口难调。

方案二
客户端

<script>
	fonctiong show(data) {
		// alert(data);
		document.write(data);
	};
script>
<script src='http://127.0.0.1:8080'>script>

服务端

let weather = '晴 22-30℃';
let string = `show(${weather})`;
res.write(string);
res.end();
// 问题:服务端将函数名固定了,
// 众口难调。

方案三(请求时携带参数)
客户端

<script>
	fonctiong show(data) {
		// alert(data);
		document.write(data);
	};
script>
<script src='http://127.0.0.1:8080?fname=show'>script>

服务端

const http = require('http');
const url = require('url');
http.createServer((req, res)=>{
	// 不使用 express 框架
	let fname = url.parse(req.url, true).query.fname;
	let weather = '晴 22-30℃';
	let string = `${fname}(${weather})`;
	res.write(string);
	res.end();
});
// 问题:客户端的 

new Vue({
	el: "#app",
	data: {
		poetList: [
			{
				id: 1,
				sname: '李白',
				value: 'LiBai'
			}, {
				id: 2,
				sname: '杜甫',
				value: 'DuFu'
			}, {
				id: 3,
				sname: '贺知章',
				value: 'HeZhiZhang'
			}, {
				id: 4,
				sname: '李商隐',
				value: 'LiShangYin'
			}, {
				id: 5,
				sname: '屈原',
				value: 'QuYuan'
			}
		],
		checkList: []
	},

	// 过滤器
	filters: {
		handleId(value) {
			return 'poet' + value;
		}
	},

	// 计算属性
	computed: {
		isAll: {
			get() {
				return this.checkList.length === this.poetList.length;
			},

			set(value) {
				if (value) {
					this.poetList.forEach(item => {
						this.checkList.push(item.value);
					});
					return;
				} else {
					this.checkList = [];
				}
			}
		}
	}
});

DOM节点

1、方法

方法 描述/作用 使用 备注
getElementById() 返回带有指定 ID 的元素。
getElementsByTagName() 返回包含带有指定标签名称的所有元素的节点列表(集合/节点数组)。
getElementsByClassName() 返回包含带有指定类名的所有元素的节点列表。
appendChild() 把新的子节点添加到指定节点。
removeChild() 删除子节点。
remove() 移除自身节点。
replaceChild() 替换子节点。
insertBefore() 在指定的子节点前面插入新的子节点。
createAttribute() 创建属性节点。
createElement() 创建元素节点。
createTextNode() 创建文本节点。
getAttribute() 返回指定的属性值。
setAttribute() 把指定属性设置或修改为指定的值。

W3school - 属性


2、属性

属性 描述/作用 使用 备注
innerHTML 获取元素内容的最简单方法是使用 innerHTML 属性。
nodeName 属性规定节点的名称。
nodeValue 属性规定节点的值
nodeType 属性返回节点的类型。nodeType 是只读的。
offsetHeight 获取元素高度

W3school - 属性


3、设置 style 属性
3.1、直接设置 style 对象

1. el.style.color = '#b50029;'
2. el.style.fontSize = '30px;'
3. el.style['font-size'] = '30px;'


3.2、设置 style 属性

1. el.setAttribute('style', 'color: red;')
2. el.setAttribute('style', 'font-size: 30px;')
3. el.setAttribute('style', 'font-size: 30px;')
4. el.setAttribute('style', 'color: red;' + 'font-size: 30px;' + 'background-color: green;')


3.3、设置 cssText

1. el.style.cssText = "color: red; font-size: 30px;"
2. el.style.cssText += 'background-color: yellow;'
3. el.style.cssText = "color: red; font-size: 30px;"
4. el.style.cssText = "text-decoration: line-through;"


3.4、相关链接

博客园


JavaScript之函数作用域(scopes)和函数作用域链(scope chain)

示例1

var a = 10;
function funScopes() {
	// 这里获取的是函数里面的 a
	console.log(a); // undefined
	// 这里存在变量提升
	var a = 20;
};
funScopes();
// 这里获取的是全局的 a
console.log(a); // 10

示例2

var a = 10;

function funScopes() {
	// 获取的是全局 a
	console.log(a); 
	// 10
	// 给全局 a 重新赋值
	a = 20;
}

funScopes();
// 获取的是改变值之后的全局 a
console.log(a); 
// 20

示例3

var a = 10;

function funScopes(a) {
	// 形参变量会自动在函数中 var a;   
	// 获取的是传进来的值 
	console.log(a); 
	// 10
	// 给当前函数中的 a 重新赋值
	a = 20;
}

funScopes(a);
// 获取的是全局中的 a
console.log(a); 
// 10

示例4

var a = 10;

function funScopes() {
	// 函数作用域一开始就已经定义好,
	// 跟函数在哪里调用无关。
	// 所以这里的 a 是全局的
	console.log(a); 
	// 10
}

(function() {
	var a = 100;
	
	funScopes();
})();

html之标签元素设置自定义属性、setAttribute、getAttribute

第一种方法

// 设置
btnList[i].myIndex = i;

// 获取
this.myIndex;

第二种方法

// 设置
btnList[i].setAttribute('data-index', i);

// 获取
this.getAttribute('data-index');

web前端之vue在父组件中调用子组件的方法函数


1、父组件
1.1、html部分

<button @click="trigger">触发方法button>
<childrens ref="childs">childrens>

1.2、JavaScript部分

methods: {
	trigger() {
		this.$refs.childs.useInPar("调用子组件中的方法");
	}
}

2、子组件
2.1、JavaScript部分

methods: {
	useInPar(str) {
		console.log(str);
	}
}

JavaScript之对象打点的访问方式

功能

可以在dataObj对象中,寻找用连续点符号的keyName属性。


代码

// export default function lookup(dataObj, keyName) {
function lookup(dataObj, keyName) {
	// 看看keyName中有没有点符号,但是不能是.本身
    if (keyName.indexOf('.') != -1 && keyName != '.') {
        // 如果有点符号,那么拆开成数组
        var keys = keyName.split('.');
        // 设置一个临时变量,这个临时变量用于周转,一层一层找下去。
        var temp = dataObj;
        
        // 每找一层,就把它设置为新的临时变量
        for (let i = 0; i < keys.length; i++) temp = temp[keys[i]];
        
        return temp;
    }
    
    // 如果这里面没有点符号
    return dataObj[keyName];
}

let objData = {
	a: {
		b: {
			c: 100
		},
		d: {
			e: 1000
		}
	},
	f: 10000,
	g: {
		h: 100000
	}
};

console.log(lookup(objData, 'a.b.c')); 
// 100
console.log(lookup(objData, 'a.d.e')); 
// 1000
console.log(lookup(objData, 'f')); 
// 10000
console.log(lookup(objData, 'g.h')); 
// 100000

vue返回刷新

1、html部分

<keep-alive>
	<div>内容div>
keep-alive>

包裹内容,不然activated生命周期无效。


2、JavaScript部分

activated() {
	// 需要获取数据的接口在此调用
},

3、相关链接
1、vue中keep-alive、activated的探讨和使用
2、vue中keep-alive的使用及详解


vue实现菜单右键

去掉浏览器默认事件,添加自定义事件。 @contextmenu.prevent=“rightClick()” // rightClick 为自定义事件。

<button type="button" @contextmenu.prevent="rightClick()">保存button>
methods: { 
	rightClick () {
		alert("触发了右击事件");
	},
}

vue+html5+原生dom+原生JavaScript实现跨区域拖放

1、关键代码

// 放
function drop(ev) {
	let data = ev.dataTransfer.getData("Text"),
		i = ev.path[1].getAttribute("i"),
		text = document.getElementById(data).cloneNode(true).innerText.trim();

	if (i == null) return alert('请放置在文件名上');
	if (app.fileS[i].divs.includes(text)) return alert('不能放重复数据');
	app.fileS[i].divs.push(text);
	for (let is = 0; is < app.fileS.length; is++) {
		if (i == is) {
			app.fileS[is].isShow = true;
		} else {
			app.fileS[is].isShow = false;
		}
	}
}

2、完整代码
gitee(码云) - mj01分支 - copyDragAndDrop 文件


vue实现跨区域拖放

1、关键代码

dragend(item) {
	console.log(item);
	if (this.oldItem != this.newItem) {
		let oldIndex = this.List.indexOf(this.oldItem);
		let newIndex = this.List.indexOf(this.newItem);

		let oldflag = false
		let newflag = false

		if (oldIndex === -1) {
			oldflag = true
			oldIndex = this.list.indexOf(this.oldItem);
		}

		if (newIndex === -1) {
			newflag = true
			newIndex = this.list.indexOf(this.newItem);
		}

		let newList = [...this.List]; // 中间数组,用于交换两个节点
		let newlist = [...this.list]; // 中间数组,用于交换两个节点

		if (!oldflag) {
			newList.splice(oldIndex, 1);
		} else {
			newlist.splice(oldIndex, 1);
		}

		if (!newflag) {
			newList.splice(newIndex, 0, this.oldItem);
		} else {
			newlist.splice(newIndex, 0, this.oldItem);
		}

		// 删除老的节点
		// newList.splice(oldIndex, 1);
		// // 在列表目标位置增加新的节点
		// newList.splice(newIndex, 0, this.oldItem);
		// // 更新this.List,触发transition-group的动画效果
		this.List = [...newList];
		this.list = [...newlist];
	}
}

2、完整代码
gitee(码云) - mj01分支 - dragAndDrop 文件


ES6模块化

1、默认导出/导入
1.1、默认导出

let n1 = 10;
let n2 = 20;
function show() {};

export default {
	n1,
	show
};

// 一个.js文件中不能有两个
// export default { };

// export default {
//	n2,
// };

1.2、默认导入

import m1 from './01.默认导出.js';

console.log(m1); 
// { n1: 10, show: [Function: show] }

2、按需导出/导入
2.1、按需导出

export let s1 = 'aaa';
export let s2 = 'ccc';
export function say() {};

export default {
	a: 20
};

2.2、按需导入

import info, { s1, s2 as str2, say } from './03.按需导出.js';
// as: 重命名 把 s2 重命名为 str2
console.log(s1); // aaa
console.log(str2); // ccc
console.log(say); // [Function: say]
console.log(info); // { a: 20 }

3、直接运行模块中的代码
3.1、需要运行的代码文件

for (let i = 0; i < 3; i++) {
	console.log(i);
	// 0
	// 1
	// 2
}

3.2、运行代码的文件

import './05.直接运行模块中的代码.js';

vue实现滚动条点击切换距离、滚动条隐藏样式

1、html

DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>点击移动title>
    <link rel="stylesheet" href="./index.css">
head>

<body>
	<div id="app">
        <div class="content_box" ref="refScrollLeft">
            <div class="item" v-for="item in 24" :key="item" @click="clickMovement(item)">{{item}}
            div>
        div>
    div>

    <script src="/node_modules/vue/dist/vue.js">script>
    <script src="./index.js">script>
body>

html>

2、JavaScript

new Vue({
    el: "#app",
    data() {
        return {
            oldVal: 0,
            scrollLeft: 0
        }
    },
    mounted() {

    },

    methods: {
        clickMovement(val) {
            if (val > this.oldVal) {
                this.scrollLeft = this.scrollLeft + 180;
            } else if (val < this.oldVal) {
                this.scrollLeft = this.scrollLeft - 180;
            } else if (val == this.oldVal) {
                this.scrollLeft = this.scrollLeft
            }
            // 关键代码
            this.$refs.refScrollLeft.scrollLeft = this.scrollLeft;
            this.oldVal = val;
        }
    }
});

3、css

#app {
    position: absolute;
    left: 50%;
    transform: translate(-50%, );
}

.content_box {
    width: 300px;
    display: flex;
    align-items: center;
    padding: 1em 0;
    overflow-x: scroll;
}

.content_box::-webkit-scrollbar {
    width: 0 !important;
    height: 0 !important;
}

.item {
    height: 2em;
    text-align: center;
    line-height: 2em;
    background-color: #686868;
    color: #fff;
	margin: 0 10px;
    padding: 0 1em;
    cursor: pointer;
}

vue3的生命周期

1、父组件

<template>
	<h2>Apph2>
	<button @click="isShow=!isShow">切换button>
	<hr>
	<Child v-if="isShow"/>
template>

import Child from './Child.vue';

export default {
  data () {
    return {
      isShow: true
    }
  },
  components: {
    Child
  }
};

2、子组件

<template>
	<div class="about">
		<h2>msg: {{msg}}h2>
		<hr>
		<button @click="update">更新button>
	div>
template>

import {
  ref,
  onMounted,
  onUpdated,
  onUnmounted, 
  onBeforeMount, 
  onBeforeUpdate,
  onBeforeUnmount
} from "vue";

export default {
  beforeCreate () {
    console.log('2.xbeforeCreate()')
  },

  created () {
    console.log('2.xcreated')
  },

  beforeMount () {
    console.log('2.xbeforeMount')
  },

  mounted () {
    console.log('2.xmounted')
  },

  beforeUpdate () {
    console.log('2.xbeforeUpdate')
  },

  updated () {
    console.log('2.xupdated')
  },

  beforeUnmount () {
    console.log('2.xbeforeUnmount')
  },

  unmounted () {
     console.log('2.xunmounted')
  },
  
  setup() {
    const msg = ref('abc');
    const update = () => {
      msg.value += '--';
    };

    onBeforeMount(() => {
      console.log('3.0--onBeforeMount')
    });

    onMounted(() => {
      console.log('3.0--onMounted')
    });

    onBeforeUpdate(() => {
      console.log('3.0--onBeforeUpdate')
    });

    onUpdated(() => {
      console.log('3.0--onUpdated')
    });

    onBeforeUnmount(() => {
      console.log('3.0--onBeforeUnmount')
    });

    onUnmounted(() => {
      console.log('3.0--onUnmounted')
    });
    
    return {
      msg,
      update
    };
  }
};

3、2.x与3.x生命周期执行顺序

3.x中生命周期执行顺序比2.x快,也就是先于2.x的生命周期执行。


vue3手写isRef、isReactive、isReadonly、isProxy

1、包含的功能及函数

isRef、isReactive、isReadonly、isProxy、shallowReactive、reactive、shallowReadonly、readonly、shallowRef、ref


2、函数实现

// 定义一个reactiveHandler处理对象
const reactiveHandler = {
  // 获取属性值
  get(target, prop) {
    if (prop === '_is_reactive') return true;
    const result = Reflect.get(target, prop);
    console.log('拦截了读取数据', prop, result);
    return result;
  },
  // 修改属性值或者是添加属性
  set(target, prop, value) {
    const result = Reflect.set(target, prop, value);
    console.log('拦截了修改数据或者是添加属性', prop, value);
    return result;
  },
  // 删除某个属性
  deleteProperty(target, prop) {
    const result = Reflect.deleteProperty(target, prop);
    console.log('拦截了删除数据', prop);
    return result;
  }
}

// 01--------------------------------------
// shallowReactive
// 定义一个shallowReactive函数,传入一个目标对象
function shallowReactive(target) {
  // 判断当前的目标对象是不是object类型(对象/数组)
  if (target && typeof target === 'object') {
    return new Proxy(target, reactiveHandler);
  }
  // 如果传入的数据是基本类型的数据,那么就直接返回
  return target;
}

// 02--------------------------------------
// reactive
// 定义一个reactive函数,传入一个目标对象
function reactive(target) {
  // 判断当前的目标对象是不是object类型(对象/数组)
  if (target && typeof target === 'object') {
    // 对数组或者是对象中所有的数据进行reactive的递归处理
    // 先判断当前的数据是不是数组
    if (Array.isArray(target)) {
      // 数组的数据要进行遍历操作0
      target.forEach((item, index) => {
        // 如果数组中还有数组
        // 使用递归
        target[index] = reactive(item);
      });
    } else {
      // 再判断当前的数据是不是对象
      // 对象的数据也要进行遍历的操作
      Object.keys(target).forEach(key => {
        target[key] = reactive(target[key]);
      });
    }
    return new Proxy(target, reactiveHandler);
  }
  // 如果传入的数据是基本类型的数据,那么就直接返回
  return target;
}

// ===============================================================
// 定义了一个readonlyHandler处理器
const readonlyHandler = {
  get(target, prop) {
    if (prop === '_is_readonly') return true;
    const result = Reflect.get(target, prop);
    console.log('拦截到了读取数据了', prop, result);
    return result;
  },
  set(target, prop, value) {
    console.warn('只能读取数据,不能修改数据或者添加数据');
    return true;
  },
  deleteProperty(target, prop) {
    console.warn('只能读取数据,不能删除数据');
    return true;
  }
}

// 03--------------------------------------
// shallowReadonly
// 定义一个shallowReadonly函数
function shallowReadonly(target) {
  // 需要判断当前的数据是不是对象
  if (target && typeof target === 'object') {
    return new Proxy(target, readonlyHandler);
  }
  return target;
}

// 04--------------------------------------
// readonly
// 定义一个readonly函数
function readonly(target) {
  // 需要判断当前的数据是不是对象
  if (target && typeof target === 'object') {
    // 判断target是不是数组
    if (Array.isArray(target)) {
      // 遍历数组
      target.forEach((item, index) => {
        target[index] = readonly(item);
      });
    } else {
      // 判断target是不是对象
      // 遍历对象
      Object.keys(target).forEach(key => {
        target[key] = readonly(target[key]);
      });
    }
    return new Proxy(target, readonlyHandler);
  }
  // 如果不是对象或者数组,那么直接返回
  return target;
}

// ===============================================================
// 05--------------------------------------
// shallowRef
// 定义一个shallowRef函数
function shallowRef(target) {
  return {
    // 保存target数据保存起来
    _value: target,
    get value() {
      console.log('劫持到了读取数据');
      return this._value;
    },
    set value(val) {
      console.log('劫持到了修改数据,准备更新界面', val);
      this._value = val;
    }
  }
}

// 06--------------------------------------
// ref
// 定义一个ref函数
function ref(target) {
  target = reactive(target);
  return {
    _is_ref: true, // 标识当前的对象是ref对象
    // 保存target数据保存起来
    _value: target,
    get value() {
      console.log('劫持到了读取数据');
      return this._value;
    },
    set value(val) {
      console.log('劫持到了修改数据,准备更新界面', val);
      this._value = val;
    }
  }
}

// ===============================================================
// 定义一个函数isRef,判断当前的对象是不是ref对象
function isRef(obj) {
  return obj && obj._is_ref;
}

// 定义一个函数isReactive,判断当前的对象是不是reactive对象
function isReactive(obj) {
  return obj && obj._is_reactive;
}

// 定义一个函数isReadonly,判断当前的对象是不是readonly对象
function isReadonly(obj) {
  return obj && obj._is_readonly;
}

// 定义一个函数isProxy,判断当前的对象是不是reactive对象或者readonly对象
function isProxy(obj) {
  return isReactive(obj) || isReadonly(obj);
}

3、函数调用

// 01--------------------------------------
// shallowReactive
const proxyUser1 = shallowReactive({
  name: '小明',
  car: {
    color: 'red'
  }
});
// 拦截到了读和写的数据
proxyUser1.name += '==';
// 拦截到了读取数据,但是拦截不到写的数据
proxyUser1.car.color + '==';
// 拦截到了删除数据
delete proxyUser1.name;
// 只拦截到了读,但是拦截不到删除
delete proxyUser1.car.color;

// 02--------------------------------------
// reactive
const proxyUser2 = reactive({
  name: '小明',
  car: {
    color: 'red'
  }
});
// 拦截到了读和修改的数据
proxyUser2.name += '==';
// 拦截到了读和修改的数据
proxyUser2.car.color = '==';
// 拦截了删除
delete proxyUser2.name;
// 拦截到了读和拦截到了删除
delete proxyUser2.car.color;

// 03--------------------------------------
// shallowReadonly
const proxyUser3 = shallowReadonly({
  name: '小明',
  cars: ['奔驰', '宝马']
});
// 可以读取
console.log(proxyUser3.name);
// 不能修改
proxyUser3.name = '==';
// 不能删除
delete proxyUser3.name;
// 拦截到了读取,可以修改
proxyUser3.cars[0] = '奥迪';
// 拦截到了读取,可以删除
delete proxyUser3.cars[0];

// 04--------------------------------------
// readonly
const proxyUser4 = readonly({
  name: '小明',
  cars: ['奔驰', '宝马']
});
// 拦截到了读取
console.log(proxyUser4.name);
console.log(proxyUser4.cars[0]);
// 只读的
proxyUser4.name = '哈哈';
// 只读的
proxyUser4.cars[0] = '哈哈';
delete proxyUser4.name;
delete proxyUser4.cars[0];

// 05--------------------------------------
// shallowRef
const ref1 = shallowRef({
  name: '小明',
  car: {
    color: 'red'
  }
});
console.log(ref1.value);
// 劫持到
ref1.value = '==';
// 劫持不到
ref1.value.car = '==';

// 06--------------------------------------
// ref
const ref2 = ref({
  name: '小明',
  car: {
    color: 'red'
  }
});
console.log(ref2.value);
// 劫持到
ref2.value = '==';
// 劫持到
ref2.value.car = '==';

// 07--------------------------------------
console.log(isRef(ref({})));
console.log(isReactive(reactive({})));
console.log(isReadonly(readonly({})));
console.log(isProxy(reactive({})));
console.log(isProxy(readonly({})));

vue3手写ref、深的ref

1、函数实现

// 定义一个reactive函数,传入一个目标对象
function reactive(target) {
  // 判断当前的目标对象是不是object类型(对象/数组)
  if (target && typeof target === 'object') {
    // 对数组或者是对象中所有的数据进行reactive的递归处理
    // 先判断当前的数据是不是数组
    if (Array.isArray(target)) {
      // 数组的数据要进行遍历操作0
      target.forEach((item, index) => {
        // 如果数组中还有数组
        // 使用递归
        target[index] = reactive(item);
      });
    } else {
      // 再判断当前的数据是不是对象
      // 对象的数据也要进行遍历的操作
      Object.keys(target).forEach(key => {
        target[key] = reactive(target[key]);
      });
    }
    return new Proxy(target, reactiveHandler);
  }
  // 如果传入的数据是基本类型的数据,那么就直接返回
  return target;
}

// 定义一个ref函数
function ref(target) {
  target = reactive(target);
  return {
    _is_ref: true, // 标识当前的对象是ref对象
    // 保存target数据保存起来
    _value: target,
    get value() {
      console.log('劫持到了读取数据');
      return this._value;
    },
    set value(val) {
      console.log('劫持到了修改数据,准备更新界面', val);
      this._value = val;
    }
  }
}

2、函数调用

const ref2 = ref({
      name: '小明',
      car: {
        color: 'red'
      }
});
console.log(ref2.value);
// 劫持到
ref2.value = '==';
// 劫持到
ref2.value.car = '==';

vue3手写shallowRef、浅的ref

1、函数实现

// 定义一个shallowRef函数
function shallowRef(target) {
  return {
    // 保存target数据保存起来
    _value: target,
    get value() {
      console.log('劫持到了读取数据');
      return this._value;
    },
    set value(val) {
      console.log('劫持到了修改数据,准备更新界面', val);
      this._value = val;
    }
  }
}

2、函数调用

const ref1 = shallowRef({
      name: '小明',
      car: {
        color: 'red'
      }
});
console.log(ref1.value);
// 劫持到
ref1.value = '==';
// 劫持不到
ref1.value.car = '==';

vue+mousedown实现全屏拖动,全屏投掷

1、html

DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>鼠标滑动title>
    <link rel="stylesheet" href="./index.css">
head>

<body>
	<div id="app">
        <div class="ctn ctn1">
            <div class="sub sub1" v-for="(site, index) in list1">
                <div class="dragCtn fixed" @mousedown="mousedown(site, $event)"
                    @mousemove.prevent='mousemove(site, $event)' @mouseup='mouseup(site, $event)'>
                    {{ site.name }}
                div>
            div>
        div>
        <div class="ctn ctn2">
            <div class="sub sub2" v-for="(site, index) in list2">
                <div class="dragCtn">
                    {{ index }} : {{ site.name }}
                div>
            div>
        div>
    div>

    <script src="/node_modules/vue/dist/vue.js">script>
    <script src="./index.js">script>
body>

html>

2、JavaScript

new Vue({
    el: '#app',
    data: {
        list1: [{ name: '拖动我', index: 0 }],
        list2: [{ name: 'a', index: 0 }, { name: 'b', index: 1 }, { name: 'c', index: 2 }, { name: 'd', index: 3 }],
        vm: '',
        sb_bkx: 0,
        sb_bky: 0,
        is_moving: false
    },
    methods: {
        mousedown: function (site, event) {
            var startx = event.x;
            var starty = event.y;
            this.sb_bkx = startx - event.target.offsetLeft;
            this.sb_bky = starty - event.target.offsetTop;
            this.is_moving = true;
        },
        mousemove: function (site, event) {
            var endx = event.x - this.sb_bkx;
            var endy = event.y - this.sb_bky;
            var _this = this
            if (this.is_moving) {
                event.target.style.left = endx + 'px';
                event.target.style.top = endy + 'px';
            }
        },
        mouseup: function (e) {
            this.is_moving = false;
        }
    }
});

3、css

.ctn {
    line-height: 50px;
    cursor: pointer;
    font-size: 20px;
    text-align: center;
    float: left;
}

.sub:hover {
    background: #e6dcdc;
    color: white;
    width: 100px;
}

.ctn1 {
    border: 1px solid green;
    width: 100px;
}

.ctn2 {
    border: 1px solid black;
    width: 100px;
    margin-left: 50px;
}

.fixed {
    width: 100px;
    height: 100px;
    position: fixed;
    background: red;
    left: 10px;
    top: 10px;
    cursor: move;
}

vue+element中的InfiniteScroll无限滚动之实现分页请求数据、滚动加载请求接口

1、html部分

<div
	class="Record-frame"
	v-infinite-scroll="infiniteScroll"
	:infinite-scroll-disabled="isInfiniteScroll"
	:infinite-scroll-distance="5"
>
	<div v-for="item in dataList" :key="item.id">
		<div>{{ item.name }}div>
		<div v-text="item.age">div>
	div>
div>

2、css部分

.Record-frame {
	height: 470px;
	overflow-y: scroll;
}

3、JavaScript部分

export default {
	name: "LinkageRecord",
	data() {
		return {
			dataList: [],
			total: 0,
			query: {
				pageNum: 1,
				pageSize: 10,
			},
			isInfiniteScroll: false, // 控制滚动禁用
		};
	},

	methods: {
		// 滚动条触底
		infiniteScroll() {
			this.isInfiniteScroll = true;
			this.getLinkRecordV1ListS();
		},

		// 获取数据
		getData() {
			getAxios(this.query).then((response) => {
				let { total, records } = response;
				this.dataList = [...this.dataList, ...records];
				this.total = total;
				this.query.pageNum += 1;

				if (records.length < this.query.pageSize || this.dataList.length == this.total) {
          			this.isInfiniteScroll = true;
          			return false;
        		}

        		this.isInfiniteScroll = false;
			});
		}
	}
};

原文链接

vue+elementUI实现滚动加载请求接口


vue3customRef

1、概念

创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显式控制。


2、代码示例
需求

使用customRef实现debounce的示例。


<template>
  <h2>CustomRef的使用h2>
  <input type="text" v-model="keyword" />
  <p>{{ keyword }}p>
template>

import { customRef, defineComponent, ref } from "vue";
// 自定义hook防抖的函数
// value传入的数据,将来数据的类型不确定,所以,用泛型,delay防抖的间隔时间.默认是200毫秒
function useDebouncedRef<T>(value: T, delay = 200) {
  // 准备一个存储定时器的id的变量
  let timeOutId: number;
  return customRef((track, trigger) => {
    return {
      // 返回数据的
      get() {
        // 告诉Vue追踪数据
        track();
        return value;
      },
      // 设置数据的
      set(newValue: T) {
        // 清理定时器
        clearTimeout(timeOutId);
        // 开启定时器
        timeOutId = setTimeout(() => {
          value = newValue;
          // 告诉Vue更新界面
          trigger();
        }, delay);
      },
    };
  });
}

export default defineComponent({
  name: "App",
  setup() {
    // const keyword = ref('abc')
    const keyword = useDebouncedRef("abc", 500);
    return {
      keyword,
    };
  },
});

vue3readonly与shallowReadonly

1、概念

readonly
○深度只读数据。
○获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理。
○只读代理是深层的:访问的任何嵌套 property 也是只读的。
shallowReadonly
○浅只读数据。
○创建一个代理,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换。
应用场景
○在某些特定情况下, 我们可能不希望对数据进行更新的操作, 那就可以包装生成一个只读代理对象来读取数据, 而不能修改或删除。


2、示例代码

<template>
  <h2>readonly和shallowReadonlyh2>
  <h3>state:{{ state2 }}h3>
  <hr />
  <button @click="update">更新数据button>
template>

import { defineComponent, reactive, readonly, shallowReadonly } from "vue";

export default defineComponent({
  name: "App",
  setup() {
    const state = reactive({
      name: "佐助",
      age: 20,
      car: {
        name: "奔驰",
        color: "yellow",
      },
    });
    // 只读的数据---深度的只读
    // const state2 = readonly(state)
    // 只读的数据---浅只读的
    const state2 = shallowReadonly(state);
    const update = () => {
      // state2.name += '==='
      // state2.car.name += '=='

      // state2.name+='==='
      state2.car.name += "===";
    };
    return {
      state2,
      update,
    };
  },
});

vue3toRaw与markRaw

1、概念

toRaw
返回由 reactive 或 readonly 方法转换成响应式代理的普通对象。
这是一个还原方法,可用于临时读取,访问不会被代理/跟踪,写入时也不会触发界面更新。
markRaw
标记一个对象,使其永远不会转换为代理。返回对象本身。
应用场景
有些值不应被设置为响应式的,例如复杂的第三方类实例或 Vue 组件对象。
当渲染具有不可变数据源的大列表时,跳过代理转换可以提高性能。


2、示例代码
html

<template>
  <h2>toRaw和markRawh2>
  <h3>state:{{ state }}h3>
  <hr />
  <button @click="testToRaw">测试toRawbutton>
  <button @click="testMarkRaw">测试markRawbutton>
template>

JavaScrip

import { defineComponent, markRaw, reactive, toRaw } from "vue";

interface UserInfo {
  name: string;
  age: number;
  likes?: string[];
}

export default defineComponent({
  name: "App",
  setup() {
    const state = reactive<UserInfo>({
      name: "小明",
      age: 20,
    });

    const testToRaw = () => {
      // 把代理对象变成了普通对象了,数据变化,界面不变化
      const user = toRaw(state);
      user.name += "==";
      console.log("哈哈,我好帅哦");
    };
    const testMarkRaw = () => {
      // state.likes = ['吃', '喝']
      // state.likes[0] += '=='
      // console.log(state)
      const likes = ["吃", "喝"];
      // markRaw标记的对象数据,从此以后都不能再成为代理对象了
      state.likes = markRaw(likes);
      setInterval(() => {
        if (state.likes) {
          state.likes[0] += "=";
          console.log("定时器走起来");
        }
      }, 1000);
    };
    return {
      state,
      testToRaw,
      testMarkRaw,
    };
  },
});

JavaScript实现数值转汉字大写、价格

1、代码

<input type="text" name="je" onkeyup="priceInput(this)" />
<div id="showVal">div>

function priceInput(obj) {
	document.getElementById('showVal').innerText = this.toChineseMoney(obj.value);
}

function toChineseMoney(n) {
	if (!/^(0|[1-9]\d*)(\.\d+)?$/.test(n)) return "数据非法";

	let unit = "仟佰拾亿仟佰拾万仟佰拾圆角分",
		str = "";
		n += "00";
	let p = n.indexOf('.');
	
	if (p >= 0) n = n.substring(0, p) + n.substr(p + 1, 2);

	unit = unit.substr(unit.length - n.length);
	for (let i = 0; i < n.length; i++) str += '零壹贰叁肆伍陆柒捌玖'.charAt(n.charAt(i)) + unit.charAt(i);

	return str.replace(/零(仟|佰|拾|角)/g, "零").replace(/(零)+/g, "零").replace(/零(万|亿|圆)/g, "$1").replace(/(亿)万|壹(拾)/g, "$1$2").replace(/^圆零?|零分/g, "").replace(/圆$/g, "圆整");
}

2、相关链接
2.1、CSDN-前端js价格转换为大写的
2.2、博客园-js转换金额为中文大写
2.3、博客园-js金额转换为大写


JavaScript实现价格输入控制并翻译为中文大写、结合

1、HTML部分

<div>
	<input type="text" maxlength="12" name="je" placeholder="请输入价格" onkeyup="priceInput(this)" />
	<div id="showVal">div>
div>

<script src="./index.js">script>

2、JavaScript部分

function priceInput(obj) {
	// 清除数字和点以外的字符
	obj.value = obj.value.replace(/[^\d.]/g, "");
	// 验证第一个字符是否是数字,也就是是第一个字符不能是点
	obj.value = obj.value.replace(/^\./g, "");
	// 只保留第一个点, 清除多余的点
	obj.value = obj.value.replace(/\.{2,}/g, ".");
	obj.value = obj.value.replace(".", "$#$").replace(/\./g, "").replace("$#$", ".");
	// 只能输入两个小数
	obj.value = obj.value.replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3');

	// 此处控制的是如果没有小数点,首位不能为0,类似于01、02的金额
	if (obj.value.indexOf(".") < 0 && obj.value != "") {
		if (obj.value.substr(0, 1) == '0' && obj.value.length == 2) {
			obj.value = parseFloat(obj.value);
		}
	} else { // 此处控制的是如果有小数点,整数如果大于等于两位,第一位不能为0,类似于01.16、02.77的金额
		let val = obj.value,
			beforePoint = val.split('.')[0],
			afterPoint = val.split('.')[1];
		if (beforePoint.length >= 2 && beforePoint[0] == 0) {
			obj.value = `${beforePoint.substring(1)}.${afterPoint}`;
		}
	}

	document.getElementById('showVal').innerText = this.toChineseMoney(obj.value);
}

function toChineseMoney(n) {
	let unit = "仟佰拾亿仟佰拾万仟佰拾圆角分",
		str = "";
		n += "00";
	let p = n.indexOf('.');

	if (p >= 0) n = n.substring(0, p) + n.substr(p + 1, 2);

	unit = unit.substr(unit.length - n.length);
	for (let i = 0; i < n.length; i++) str += '零壹贰叁肆伍陆柒捌玖'.charAt(n.charAt(i)) + unit.charAt(i);

	return str.replace(/零(仟|佰|拾|角)/g, "零").replace(/(零)+/g, "零").replace(/零(万|亿|圆)/g, "$1").replace(/(亿)万|壹(拾)/g, "$1$2").replace(/^圆零?|零分/g, "").replace(/圆$/g, "圆整");
}

无需循环就可以把数据添加到数组相应位置、动态往数组对象中添加数据、对象类型数据的妙用

1、关键代码

new Vue({
	el: "#app",
	data() {
		return {
			tableData: [
				{ id: 1, title: "标题-1", options: [], value: '' }, 
				{ id: 2, title: "标题-2", options: [], value: '' }, 
				{ id: 3, title: "标题-3", options: [], value: '' }
			],
		}
	},

	methods: {
		focusSelect(item) {
			if (item.id % 2 == 0) {
				item.options = [
					{ value: '1', label: 'label-1' }, 
					{ value: '3', label: 'label-3' }, 
					{ value: '5', label: 'label-5' }
				];
			} else {
				item.options = [
					{ value: '2', label: 'label-2' }, 
					{ value: '4', label: 'label-4' }, 
					{ value: '6', label: 'label-6' }
				]
			}
		}
	}
});

2、完整代码
gitee(码云) - mj01分支 - dynamicAddData 文件夹


vue之this.$set和Vue.set的使用

1、html代码

<el-row>
	<el-col>
		<span>1:span>
		<el-button type="primary" @click="clickBtn(1)">基本数据类型-赋值el-button>
		<span v-text="value">span>
	el-col>
	<el-col style="margin-top: 0.7em;">
		<span>2:span>
		<el-button type="primary" @click="clickBtn(2)">对象类型-赋值el-button>
		<span>{{objectValue.value}}span>
	el-col>
	<el-col style="margin-top: 0.7em;">
		<span>3:span>
		<el-button type="primary" @click="clickBtn(3)">对象纯数组类型-赋值el-button>
		<span>{{objectArrayValue.value[0]}}span>
	el-col>
	<el-col style="margin-top: 0.7em;">
		<span>4:span>
		<el-button type="primary" @click="clickBtn(4)">对象数组对象类型-赋值el-button>
		<span>{{objectArrayObjectValue.value[0].value}}span>
	el-col>
	<el-col style="margin-top: 0.7em;">
		<span>5:span>
		<el-button type="primary" @click="clickBtn(5)">纯数组类型-赋值el-button>
		<span>{{arrayValue[0]}}span>
	el-col>
	<el-col style="margin-top: 0.7em;">
		<span>6:span>
		<el-button type="primary" @click="clickBtn(6)">数组对象类型-赋值el-button>
		<span>{{arrayObjectValue[0].value}}span>
	el-col>
	<el-col style="margin-top: 0.7em;">
		<span>7:span>
		<el-button type="primary" @click="clickBtn(7)">数组对象纯数组类型-赋值el-button>
		<span>{{arrayObjectArrayValue[0].value[0]}}span>
	el-col>
el-row>

2、JavaScript代码

clickBtn(type) {
	if (type == 1) {
		this.value = type; // 更新成功
		// this.$set(this, 'value', type); // 更新成功
		// Vue.set(this, 'value', type); // 更新成功
	} else if (type == 2) {
		this.objectValue.value = 2; // 更新成功
		// this.$set(this.objectValue, 'value', type); // 更新成功
		// Vue.set(this.objectValue, 'value', type); // 更新成功
	} else if (type == 3) {
		// this.objectArrayValue.value[0] = type; // 更新失败
		this.$set(this.objectArrayValue.value, 0, type); // 更新成功
		// Vue.set(this.objectArrayValue.value, 0, type); // 更新成功
	} else if (type == 4) {
		this.objectArrayObjectValue.value[0].value = type; // 更新成功
		// this.$set(this.objectArrayObjectValue.value[0], 'value', type); // 更新成功
		// Vue.set(this.objectArrayObjectValue.value[0], 'value', type); // 更新成功
	} else if (type == 5) {
		// this.arrayValue[0] = type; // 更新失败
		this.$set(this.arrayValue, 0, type); // 更新成功
		// Vue.set(this.arrayValue, 0, type); // 更新成功
	} else if (type == 6) {
		this.arrayObjectValue[0].value = type; // 更新成功
		// this.$set(this.arrayObjectValue[0], 'value', type); // 更新成功
		// Vue.set(this.arrayObjectValue[0], 'value', type); // 更新成功
	} else if (type == 7) {
		// this.arrayObjectArrayValue[0].value[0] = type; // 更新失败
		// this.$set(this.arrayObjectArrayValue[0].value, 0, type); // 更新成功
		Vue.set(this.arrayObjectArrayValue[0].value, 0, type); // 更新成功
	}
}

3、完成代码
gitee(码云) - mj01分支 - vue_set 文件夹


4、相关链接
CSDN-vue中的$set和Vue.set方法


vue3手写reactive、深的劫持、深的监视、深的响应数据

1、函数实现

// 定义一个reactive函数,传入一个目标对象
function reactive(target) {
  // 判断当前的目标对象是不是object类型(对象/数组)
  if (target && typeof target === 'object') {
    // 对数组或者是对象中所有的数据进行reactive的递归处理
    // 先判断当前的数据是不是数组
    if (Array.isArray(target)) {
      // 数组的数据要进行遍历操作0
      target.forEach((item, index) => {
        // 如果数组中还有数组
        // 使用递归
        target[index] = reactive(item);
      });
    } else {
      // 再判断当前的数据是不是对象
      // 对象的数据也要进行遍历的操作
      Object.keys(target).forEach(key => {
        target[key] = reactive(target[key]);
      });
    }
    return new Proxy(target, reactiveHandler);
  }
  // 如果传入的数据是基本类型的数据,那么就直接返回
  return target;
}

// 定义一个reactiveHandler处理对象
const reactiveHandler = {
  // 获取属性值
  get(target, prop) {
    if (prop === '_is_reactive') return true;
    const result = Reflect.get(target, prop);
    console.log('拦截了读取数据', prop, result);
    return result;
  },
  // 修改属性值或者是添加属性
  set(target, prop, value) {
    const result = Reflect.set(target, prop, value);
    console.log('拦截了修改数据或者是添加属性', prop, value);
    return result;
  },
  // 删除某个属性
  deleteProperty(target, prop) {
    const result = Reflect.deleteProperty(target, prop);
    console.log('拦截了删除数据', prop);
    return result;
  }
}

2、调用函数

const proxyUser2 = reactive({
      name: '小明',
      car: {
        color: 'red'
      }
});
// 拦截到了读和修改的数据
proxyUser2.name += '==';
// 拦截到了读和修改的数据
proxyUser2.car.color = '==';
// 拦截了删除
delete proxyUser2.name;
// 拦截到了读和拦截到了删除
delete proxyUser2.car.color;

JavaScript之整数翻转、包括负整数、Number、String、substr、substring

function integerFlip(params) {
	// 0不是整数
	// 如果没有此判断
	// 值为0会进入!Number(params)判断
	if (params == 0 || params == '0') return Number(params);
	if (!Number(params)) return '请输入整数!';
	
	// 因为数字没有length属性
	// 所以需要转为字符串
	params = String(params);
	// 从末尾开始遍历字符串
	let i = params.length,
	    // 遍历的结果
	    result = '';
	
	for (; i > 0; i--) result += params[i - 1];
	
	if (result.substr(0) == '0') result = `-${result.substring(1)}`;
	if (result.substr(-1) == '-') result = `-${result.substring(0, result.length - 1)}`;
	if (result.substr(-1) == '+') result = `+${result.substring(0, result.length - 1)}`;
	
	return Number(result);
}

console.log(integerFlip(123));
// 321
console.log(integerFlip(-123));
// -321
console.log(integerFlip(130));
// 31
console.log(integerFlip(-130));
// -31
console.log(integerFlip(+170));
// 71
console.log(integerFlip(0));
// 0
console.log(integerFlip('123柒'));
// 请输入整数!

JavaScript之数据改造、后端只返回有数据的时段,没数据的时段需要前端自己补全、isArray、throw、forEach、hasOwnProperty、call、for in、push

function handleApacheECharts(existenceData) {
	if (!Array.isArray(existenceData)) throw new Error('not an array.');
	if (!existenceData[0].time) throw new Error('data error.');
	
	let obj = {
		1: { time: "08:00-09:00", value: 0, },
		2: { time: "09:00-10:00", value: 0, },
		3: { time: "10:00-11:00", value: 0, },
		4: { time: "11:00-12:00", value: 0, },
		5: { time: "12:00-13:00", value: 0, },
		6: { time: "13:00-14:00", value: 0, },
		7: { time: "14:00-15:00", value: 0, },
		8: { time: "15:00-16:00", value: 0, },
		9: { time: "16:00-17:00", value: 0, },
	},
		arr = {
			time: [],
			value: []
		};
	
	existenceData.forEach(item => {
		for (const key in item) {
			if (Object.hasOwnProperty.call(item, key)) {
				if (key === 'time') obj[item[key] - 7].value = item.value;
			}
		}
	});
	
	for (const j in obj) {
		if (Object.hasOwnProperty.call(obj, j)) {
			let item = obj[j];
			for (const key in item) {
				if (Object.hasOwnProperty.call(item, key)) {
					if (key === "time") {
						arr.time.push(item[key]);
					} else if (key === "value") {
						arr.value.push(item[key]);
					}
				}
			}
		}
	}
	
	return arr;
}

console.log(handleApacheECharts([{ time: '10', value: 30 }, { time: '16', value: 10 }, { time: '16', value: 70 }]));
console.log(handleApacheECharts());
console.log(handleApacheECharts(''));
console.log(handleApacheECharts(1));
console.log(handleApacheECharts({}));
console.log(handleApacheECharts([1, 2]));

JavaScript实现文字转表情包、RegExp、正则、matchAll、createDocumentFragment、append、createElement、append、remove、slic

let str = '[左哼哼]你好[微笑]世界[哈哈]',
    str2 = '你好
世界'
; function scrmTextEmojiRenderFn(value) { const reg = /\[+[^\[\]]+\]/g; if (value === null || value === undefined || value === '') value = ''; const fragment = document.createDocumentFragment(); let renderRes = value, renderArr = [...value.matchAll(reg)]; if (renderArr.length > 0) { // 匹配[]为主的表情 // 用reg.test(value)做判断,renderArr的少了第一项,不明原因 let renderResArr = []; // 专门处理表情所处的位置,表情的长度 renderArr.forEach(item => { let renderItemTemplate = { name: '', index: '', length: '', }; renderItemTemplate.name = item[0]; renderItemTemplate.index = item['index']; renderItemTemplate.length = renderItemTemplate.name.length; renderResArr.push(renderItemTemplate); }) const renderEmojiFn = (name, spanDom, renderRes) => { // 专门处理表情的渲染 // if (emojisAmap[name]) {// 字符串携带有[] // spanDom.classList.add('emoji_a'); // spanDom.classList.add(emojisAmap[name]); // } else { spanDom.classList.remove('chat-emoji'); spanDom.innerText = "[" + name + "]"; // } renderRes.append(spanDom); }; renderRes = document.createDocumentFragment(); renderResArr.forEach((item, i) => { // 遍历筛选出来的标签,去源数据里面组装数据。 // 根据源数据中的表情出现的位置顺序, // 去进行组装文字+标签+文字的数据 // 根据每个出现的表情的前面是否有文字, // 标签的后面是否有文字进行拼装数据 let { name, index, length } = item; name = name.split("[")[1].split("]")[0]; let spanDom = document.createElement('span'); spanDom.classList.add('chat-emoji'); let renderText = '', rendertextSpan = document.createElement('span'); if (i === 0) { // 第一项===>标签的[出现在第一项 renderText = value.slice(0, index); rendertextSpan.innerText = rendertextSpan; renderRes.append(rendertextSpan); // 文字 renderEmojiFn(name, spanDom, renderRes); // 表情 if (renderResArr.length === 1) { // 当前筛选出来的表情只有一个数量的时候 let nextRendertext = value.slice(index + length), nextRendertextSpan = document.createElement('span'); nextRendertextSpan.innerText = nextRendertext; renderRes.append(nextRendertextSpan); // 文字 } } else if (i === renderResArr.length - 1) { // 最后一项 renderText = value.slice(length + index); // 在表情的后面文字 let preItem = renderResArr[i - 1], preIndex = preItem.index + preItem.length, renderPreText = value.slice(preIndex, index), // 在表情的前面的文字 renderPreTextSpan = document.createElement('span'); renderPreTextSpan.innerText = renderPreText; renderRes.append(renderPreTextSpan); renderEmojiFn(name, spanDom, renderRes); rendertextSpan.innerText = renderText; renderRes.append(rendertextSpan); } else { let preItem = renderResArr[i - 1], preIndex = preItem.index + preItem.length; renderText = value.slice(preIndex, index); rendertextSpan.innerText = renderText; renderRes.append(rendertextSpan); renderEmojiFn(name, spanDom, renderRes); } }) } else { const renderResSpan = document.createElement('span'); renderResSpan.innerText = renderRes; renderRes = renderResSpan; } fragment.append(renderRes); return fragment; } console.log(scrmTextEmojiRenderFn(str)); console.log(scrmTextEmojiRenderFn(str2));

JavaScript之原型链

1、基础示例

Professor.prototype.tSkill = 'JavaScript';
function Professor() {}
var professor = new Professor();

Teacher.prototype = professor;
function Teacher() {
	this.mSkill = 'html';
}
var teacher = new Teacher();

Student.prototype = teacher;
function Student() {
	this.pSkill = 'css';
}
var student = new Student();

console.log(student);
// Student {pSkill: 'css'}
console.log(student.tSkill);
// JavaScript
console.log(student.mSkill);
// html
console.log(student.pSkill);
// css

原型链的顶端是Object.prototype
Object.prototype下保存了toString()


2、笔试题——1

Professor.prototype.tSkill = 'JavaScript';
function Professor() {}
var professor = new Professor();

Teacher.prototype = professor;
function Teacher() {
this.mSkill = 'html';
this.success = {
alibaba: '28',
tencent: '30'
};
}
var teacher = new Teacher();

Student.prototype = teacher;
function Student() {
this.pSkill = 'css';
}
var student = new Student();

student.success.baidu = '100';
student.success.alibaba = '29';
console.log(teacher);
// Professor {mSkill: 'html', success: {… }}
// mSkill: "html"
// success: {alibaba: '29', tencent: '30', baidu: '100'}
// [[Prototype]]: Professor
console.log(student);
// Student {pSkill: 'css'}

JavaScript中值在各种场景的转换规则

字符串操作环境 数字运算环境 逻辑运算环境 对象操作环境
undefined “undefined” NaN false Error
null “null” 0 false Error
非空字符串 不转换 字符串对应的数字值 true
空字符串 不转换 0 false String
0 “0” 不转换 false Number
NaN “NaN” 不转换 false Number
Infinity “Infinity” 不转换 true Number
Number.POSITIVE_INFINITY “Infinity” 不转换 true Number
Number.NEGATIVE_INFINITY “-Infinity” 不转换 true Number
Number.MAX_VALUE “1.7976931348623157e+308” 不转换 true Number
Number.MIN_VALUE “5e-324” 不转换 true Number
其他所有数字 “数字的字符串值” 不转换 true Number
true “true” 1 不转换 Boolean
false “false” 0 不转换 Boolean
对象 toString() value()或toString()或NaN true 不转换

程序员(web前端开发工程师)、手机号码、二进制、十进制、构造函数、substr、parseInt、prototype、length

function PhoneNumber() {
    this.arrayNumber = ['110', '10', '111', '11', '0', '1000', '100', '1', '101', '1001'];
    this.i = 0;
    this.len = 0;
    this.result = '';
}
PhoneNumber.prototype.calculation = function (params = '') {
    this.len = params.length;

    if (!this.len) return '长度不能为空!';
    if (!/^\d+$/.test(Number(params))) return '请输入纯数字!';

    for (; this.i < this.len;)(this.result += parseInt(this.arrayNumber[params[this.i]], 2), this.i++);

    this.result = `${this.result.substr(0, 3)} ${this.result.substr(3, 4)} ${this.result.substr(7)}`;

    return this.result;
}

let phoneNumber = new PhoneNumber();
console.log(phoneNumber.calculation('78159051872'));

JavaScript实现构造函数的封装

function Car(brand, color, displacement) {
    this.brand = brand;
    this.color = color;
    this.displacement = displacement;
    this.info = function() {
        return `颜色为${this.color}${this.brand},排量为${this.displacement}T。`;
    }
}

function Person({
    brand,
    color,
    displacement,
    name,
}) {
    Car.apply(this, [brand, color, displacement]);
    this.name = name;
    this.say = function() {
        console.log(`${this.name}买了一辆${this.info()}`);
        // 半晨买了一辆颜色为寒山暮紫的五菱星辰,排量为1.5T。
    }
}

let infomation = {
        brand: '五菱星辰',
        color: '寒山暮紫',
        displacement: '1.5',
        name: '半晨',
    },
    person = new Person(infomation);

person.say();

待定

你可能感兴趣的:(web前端,web,前端,web前端)