每一个大标题,曾经都是一篇文章,此文章内容比较散乱。
创建对象
// 使用字面量创建对象
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('元日');
编写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
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'
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
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
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 }
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
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
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]); // 荆轲
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 。
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
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]]
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
<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元,好激动
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 的值
let stringVal = "web半晨";
// 方式一
console.log(stringVal.charAt(0));
// 方式二
console.log(stringVal.substring(0, 1));
// 方式三
console.log(stringVal.substr(0, 1));
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]
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();
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));
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));
enum Gender {
Male,
Female
};
let i: { name: string, gender: Gender };
i = {
name: '孙悟空',
gender: Gender.Male // 'male'
};
console.log(i.gender === Gender.Male); // true
type myType = 1 | 2 | 3 | 4 | 5;
let k: myType;
let l: myType;
let m: myType;
k = 2; // 正常
l = 6; // 报错
m = 3; // 正常
// 使用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”吗?
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: 小白
// 自执行函数的作用是形成单独模块(块作用域),
// 防止此文件的变量或方法与其他文件的属性或方法冲突
(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();
})();
// 自执行函数的作用是形成单独模块(块作用域),
// 防止此文件的变量或方法与其他文件的属性或方法冲突
(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();
})();
// 自执行函数的作用是形成单独模块(块作用域),
// 防止此文件的变量或方法与其他文件的属性或方法冲突
(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}
})();
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 {};
},
});
// 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
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}
// 定义退出递归的条件,
// 否则进入无限循环
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
// 使用字面量定义具名函数,
// 在函数外部无法通过名字调用函数,
// 只能通过变量调用函数
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来占位
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('存在特殊字符!');
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]
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
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); // 武汉
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 指向由调用该方法的 点(.) 前的某个子对象 (实例) 来决定。
第一步
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分。
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、父函数
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、参考的原文链接
知乎-原文
1、数据类型
JavaScript共有8种数据类型。
7种基本数据类型:Null
、Undefined
、Boolean
、Number
、String
、Symbol
(ES6新增,表示独一无二的值)和BigInt
(ES10新增)。
1种引用数据类型:Object。Object里面包含Object
、Array
、Function
、Date
、RegExp
等。
总结:JavaScript不支持任何创建自定义类型的机制,而所有值最终都将是上述8种数据类型之一。
2、存储方式
2.1、原始数据类型
直接存储在栈(stack)中,占据空间小且大小固定,属于被频繁使用的数据,所以放入栈中存储。
2.2、引用数据类型
同时存储在栈(stack)和堆(heap)中,占据空间大,且大小不固定。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
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);
};
}
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和原型链
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 ,如果使用字面量的方式就没有这个问题。
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、参考的原文链接
知乎-原文
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、参考的原文链接
知乎-原文
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
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.comhttps:
//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();
待定