类型转换的相关知识点记录
let str1 = "houdunren"
console.log(typeof str1); // string
console.log(str1.substr(3)); // dunren
let cms = new String('hdcm')
console.log(typeof cms); // object
console.log(cms.substr(3)); // m
直接通过""(引号)创建的字符串类型,值能和对象一样引用方法是因为系统(解析器)隐式的变成对象来使用
Boolean
声明方式
// 语法书写
const boolean = new Boolean(false)
console.log(typeof boolean)
console.log(boolean.value)
if (boolean.valueOf()) {
console.log("goodgoodstudy")
}
// 字面量写法
let hd = false
console.log(typeof hd)
if (hd) {
console.log("daydayup")
}
boolean隐式转换原理
boolean ==》数值
不同类型进行比较,转换同一个类型(数值)
let number = 0;
console.log(number == false) // 0 == 0
let number = 89;
console.log(number == true) // 98 == 1 ? ===> false
当非0的数值做表达式运算的时候为TRUE
if (number) {
console.log('true');
}
// ====>
console.log(Boolean(number))
boolean ==》字符串
let hd = '0'
console.log(Number(hd)) // 0
console.log(hd == false) // 0 == 0 ==> true
let hd1 = '1'
console.log(Number(hd1)) // 1
console.log(hd == true) // 1 == 1 ==> true
当字符串(非空)做表达式运算的时候为TRUE
let hd = '0'
if (hd) {
console.log('true');
}
// ====>
console.log(Boolean(hd))
let hd = ''
if (!hd) {
console.log('true');
}
// ====>
console.log(Boolean(hd)) // false
boolean ==》数组
let arr = []
console.log(Number(arr)) // 0
console.log(arr == false) // 0 == 0 ==> true
let arr1 = [1]
console.log(Number(arr1)) // 1
console.log(arr1 == true) // 1 == 1 ==> true
let arr2 = [1, 2]
console.log(Number(arr2)) // NaN
console.log(arr2 == true) // NaN == 1 ==> false
当数组做表达式运算的时候为TRUE
let arr = []
if (arr) {
console.log('true');
}
// ====>
console.log(Boolean(arr)) // true
数组是应用类型,
一个应用类型转换成Boolean类型都会转换成true,比较是转换为number
显示转换Boolean类型
不是解析器自动完成,人为地控制
!
let number = 0
console.log(typeof number); // number
number = !!number
// !把Boolean取反,number==》Boolean,Boolean取反
// 一个!把Boolean取反,number==》Boolean,Boolean取反,另一个!在取反之后再取反
console.log(number);//false
let str = 'study'
console.log(!!str) // true
let arr = []
console.log(!!arr) // true
let date = new Date()
console.log(!!date) // true
Boolean()
let number = 0
console.log(Boolean(number)) // false
let str = 'study'
console.log(Boolean(str)) // true
let arr = []
console.log(Boolean(arr)) // true
let date = new Date()
console.log(Boolean(date)) // true
Boolean实例操作
while(true) {
const year = prompt('请输入现在的年份').trim()
if (!year) continue
let date = new Date()
let nowyear = date.getFullYear()
console.log(year == nowyear ? '回答正确' : '输入错误');
break
}
Number
声明方式与基本函数
let num = new Number(99)
console.log(num.valueOf()); // 99
console.log(typeof num);// object
let number = 99
console.log(number);
console.log(typeof number); // number
console.log(number.valueOf()); // 99
let num1 = number.valueOf() // valueOf()取得数值的值
console.log('num1', num1); // 99
console.log(typeof num1); // number
let str = number.toString()
console.log('str', str);
console.log(typeof str); // string
数值类型的静态方法
Number.isInteger(number)
let number1 = 99
let res1 = Number.isInteger(number1)
console.log(res1)
number.toFixed(保留几位小数)
let number2 = 99.023213
let res2 = number2.toFixed(2)
console.log(res2);
数值类型转换技巧和NaN类型
// NaN:不是一个数字
console.log(Number('study')); // NaN
console.log(2 / 'ssss'); // NaN
console.log(NaN == NaN); // false
console.log('nan', Number('NaN'));
console.log(Number.isNaN(2 / 'ssss')); // true
console.log(Object.is(2 / 'qqq', NaN)); // true
console.log(Number(false)); // 0
console.log(Number(true)); // 1
// 纯数字的字符串
let res = '12'
console.log(Number(res));
const num = document.querySelector("[name='number']").value
console.log(num);
console.log(typeof num);
// 前面是数值 + 其他类型的字符
let res2 = '12sssss'
console.log(Number(res2)); // NaN
console.log(parseInt(res2)); // 12
// 浮点数
let res3 = '12.12sssss'
console.log(Number(res3)); // NaN
console.log(parseInt(res3)); // 12
console.log(parseFloat(res3)); // 12.12
// 空对象
console.log('{}', Number({})); // NaN
console.log(Number({
valueOf: function() {
return 99
}
})); // 99
Math数学计算
console.log(Math.max(false, true)); // 1 ====> fasle = 0, true = 1
console.log(Math.max('12ppp', 13)); // NaN
// 获取数组中最大值
// Math.max不支持数组
let grade = [20, 88, 60, 99]
console.log(Math.max(grade)); // NaN
console.log(Math.max.apply(null, grade)); // 指定数组的形式传参 99
console.log(Math.ceil(5.1)); // 6
console.log(Math.ceil(5.9)); // 6
console.log(Math.floor(5.0001)); // 5
console.log(Math.floor(5.9)); // 5
求0-x之间的随机数
Math.floor(Math.random() * (x + 1))
// 获取0-5的随机数
// Math.random >= 0 && < 1
console.log(Math.floor(Math.random() * (5 + 1)));
// index: 0 ~ (student.length - 1)
// (x + 1) ===> (student.length - 1) + 1 ===> student.length
const student = ['张三', '李四', '王五', '赵六', '田七']
const index = Math.floor(Math.random() * student.length)
console.log(student[index]);
求x-y之间的随机数
x + Math.floor(Math.random() * (y - x + 1))
// 随机打印index: 2 ~ (student.length - 1)
// (y - x + 1) ===> (student.length - 1) - 2 + 1 ===> student.length
const student = ['张三', '李四', '王五', '赵六', '田七']
const index = 2 + Math.floor(Math.random() * (student.length - 2))
console.log(student[index]);
function arrayRandomValue(array, start = 0,end){
// end是元素的个数,不是 index
end = end ? end : array.length;
const index = start + Math.floor(Math.random() * (end - start))
return array[index]
}
Date
时间戳
const date = new Date()
console.log(date);
console.log(typeof date);
console.log(date * 1); // 返回时间戳
const timedate = Date.now()
计算脚本执行时间
console.time('for') // 'for': 标志位
for(var i = 0; i < 100;i++) {}
console.timeEnd('for')
根据具体的时间获取日期
const date = new Date('1990-9-22 3:22:18')
console.log(date) // Sat Sep 22 1990 03:22:18 GMT+0800 (中国标准时间)
console.log(date.getMonth()) // 8
const date1 = new Date(1990,2,22,13,22,19)
console.log(date1); // Thu Mar 22 1990 13:22:19 GMT+0800 (中国标准时间)
const param = [1990, 2, 22, 13, 22, 19]
const date2 = new Date(...param)
console.log(date2); // Thu Mar 22 1990 13:22:19 GMT+0800 (中国标准时间)
ISO(标准时间)和TIMESTAMP(时间戳)格式互换
时间戳
const date = new Date()
console.log(date * 1);
console.log(Number(date));
console.log(date.valueOf());
console.log(date.getTime());
时间戳转标准时间
const date = new Date()
const timestamp = date.valueOf()
console.log(new Date(timestamp));
封装日期格式化函数
const date = new Date('1992-2-12 10:22:12')
function dateFormat(date, format = 'YYYY-MM-DD HH:mm:ss') {
const config = {
YYYY: date.getFullYear(),
MM: date.getMonth(),
DD: date.getDate(),
HH: date.getHours(),
mm: date.getMinutes(),
ss: date.getSeconds(),
}
for (const key in config) {
format = format.replace(key, config[key])
}
return format
}
console.log(dateFormat(date, 'YYYY年MM月DD日')); // 1992年1月12日
优秀日期处理库
momentjs
<script src="http://cdn.staticfile.org/moment.js/2.24.0/moment.min.js"></script>
<script>
const date = moment('1992-2-22 10:11:11')
console.log(date.format('YYYY-MM-DD HH:mm:ss'));
</script>
出现的警告:MM处建议两位 1992-02-22
<script src="http://cdn.staticfile.org/moment.js/2.24.0/moment.min.js"></script>
<script>
const date = moment()
console.log(date.format('YYYY-MM-DD HH:mm:ss')); // 2021-12-27 23:28:12
console.log(date.add(10, 'days').format('YYYY-MM-DD HH:mm:ss')); // 2022-01-06 23:28:12
</script>
数组
// 构造函数
const arr = new Array('ss', 'gg', 'is')
console.log(arr);
// 字面量
const arr1 = [1, 2, 3]
console.log(arr1);
数组是
引用型数据
const arr = [1, 2]
arr[1] = 99
// 这里const定义的可以修改的原因是,数组是引用型数据,根据下标去修改内容,地址内容不变
console.table
多维数组操作
let arr = [['a'], ['b']
数组创建细节
let ds = ['dsjy']
ds[3] = 'sd'
console.log(ds);
let ds = new Array(1, 2, 3, 4)
console.log(ds.length); // 4
console.log(ds) // [1, 2, 3, 4]
let ds1 = new Array(6)
console.log(ds1.length); // 6
console.log(ds1) // [empty*6]
类型检测和转换
- 检测
Array.isArray([])
- 转换
// 数组转字符串
let ds = [1, 2, 3].toString()
let ds1 = String([1, 2, 3])
let ds2 = [1, 2, 3].join(",")
console.log(typeof ds); // string
console.log(ds, ds1, ds2); // 1,2,3 1,2,3 1,2,4
let str = 'dshm'
console.log(str.split("")); // ['d', 's', 'h', 'm']
console.log(Array.from(str)); // ['d', 's', 'h', 'm']
// 元素有length属性都可以转为数组
let str1 = 'dshm,ggs'
console.log(str1.split(",")); // ['dshm', 'ggs']
元素有length属性都可以转为数组
let obj = {
name: 'ds',
age: 11
}
console.log(Array.from(obj)); // []
let obj = {
name: 'ds',
age: 11,
lenght: 2
}
console.log(Array.from(obj)); // [undefined, undefined]
===>
let obj = {
0: 'ds',
1: 11,
length: 2
}
console.log(Array.from(obj)); // ['ds', 11]
Array.from
Array.from(数组, function(item) { // item为数组的每一项 })
let divs = document.querySelectorAll('div')
let arr = Array.from(divs, function(item) {
item.style.backgroundColor = 'red'
return item
})
console.log('arr', arr);
Array..of
展开语法(...)
- 数组中使用
let arr = [1, 2]
let brr = [3, 4]
arr = [...arr, ...brr] // [1, 2, 3, 4]
- 函数参数中使用
function sum(...args) {
return args.reduce((p, n) => {
return (p += n)
}, 0)
}
console.log(sum(1,2,3));
- DOM节点中使用
const div = document.querySelectorAll('div');
// dom获取的数组是伪数组,无法直接使用数组上的一些方法
// 伪数组 ==》 数组
Array.from(div, function(item){
console.log(item);
});
Array.prototype.map.call(div, function(item){
console.log(item);
});
[...div].map(function(item){
item.addEventListener('click', function(){
item.style.color = 'blue'
})
})
解构赋值
let arr = ['s', 18]
let [name, age] = arr
console.log(name, age); // s 18
function get() {
return ['d', 16]
}
let [name1, age1] = get()
console.log(name1, age1); // d 16
解构赋值的时候,需要用
let、var、const定义,不然在“use strict”(严格模式下)会报错
结构和展开运算符的整合运用
let [name, ...args] = ['好好学习', '天天向上', 2021]
console.log()
console.log(args); // ['天天向上', 2021]
let a = ['ss', ...args]
console.log(a) // ['ss', '天天向上', 2021]
-
...放在变量的位置(只能放在最后),就是收集 -
...放在值的位置,就是展开
let [name, year='天天向上'] = ['好好学习']
console.log(year) // '天天向上'
添加元素的多种操作方法
数组最后一位添加
- arr[arr.length]
- [...arr, ...brr]
- arr.push()
let arr = ['好好', '学习']
arr[arr.length] = '天天'
let ds = ['shop', 'sss']
arr = [...arr, ...ds]
arr.push('向上')
数据出栈与入栈及填充操作
- pop
- push
- unshift
- shift
- fill:arr.fill['需要填充的内容',从哪个位置开始,到哪个位置结束]
let arr = [1, 2, 3, 6]
let deleteData = arr.pop() // 被删除的数据
console.log('pop', deleteData);
let res1 = arr.push(5) // 数组的长度
console.log('push', res1);
console.log(Array(5).fill('学习')); // ['学习', '学习', '学习', '学习', '学习']
let res2 = ['好好', '天天']
res2.fill('学习', 1)
console.log(res2); // ['好好', '学习']
数组的增删改查
splice
array.
splice(start[, deleteCount[, item1[, item2[, ...]]]] )
- start:
- deleteCount
- item1, item2,...
slice
arr.slice([begin[, end])
- begin:从第几位
开始截取原数组的元素,- 省略begin,索引从0开始截取
- 超出原数组的索引范围,返回空数组
- end: 截取到第几位(包含
begin,但不包含end)- 省略end,begin开始截取到最后一位
- end > arr.length, begin开始截取到最后一位
- 为负数,在原数组中的倒数第几个元素结束抽取
slice(-2,-1)表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)
- 不改变原数组长度
- 返回值为截取的内容
let sliceArr1 = arr.slice()
console.log(sliceArr1); // [1, 2, 3, 4, 5]
let sliceArr2 = arr.slice(0, 1)
console.log(sliceArr2); // [1]
let sliceArr3 = arr.slice(1)
console.log(sliceArr3); // [2, 3, 4, 5]
let sliceArr4 = arr.slice(1, 99)
console.log(sliceArr4); // [2, 3, 4, 5]
let sliceArr5 = arr.slice(-2, -1)
console.log(sliceArr5); // [4]
let sliceArr6 = arr.slice(1, -1)
console.log(sliceArr6); // [2, 3, 4]
find
arr.find(callback[, thisArg])
- callback: 在数组每一项上执行的函数,接收 3 个参数
- element: 当前遍历到的元素
- index(可选): 当前遍历到的索引
- array(可选):数组本身
- thisArg(可选):执行回调函数时用作this的对象
返回数组中满足提供的测试函数的
第一个元素的值。否则返回undefined
var inventory = [
{name: 'apples', quantity: 2},
{name: 'bananas', quantity: 0},
{name: 'cherries', quantity: 5}
];
var obj = inventory.find(el => {
console.log('find', el);
return el.name === 'apples'
})
console.log('obj', obj); // {name: 'apples', quantity: 2}
自己写find
function find(array, callback){
for (const value of array) {
if (callback(value)) return value;
}
return undefined
}
let arr = [1,2,3,4]
let res2 = find(arr, item => {
return item == 3
})
console.log('res2', res2); // 3
Array.prototype.findValue = function (callback){
// this为调用这个函数的对象
for (const value of this) {
if (callback(value)) return value;
}
return undefined
}
let res3 = arr.findValue(function(item){
return item === 2
})
console.log('res3', res3); // 2
findIndex
arr.findIndex(callback[, thisArg])
- callback: 在数组每一项上执行的函数,接收 3 个参数
- element: 当前遍历到的元素
- index(可选): 当前遍历到的索引
- array(可选):数组本身
- thisArg(可选):执行回调函数时用作this的对象数组中通过提供测试函数的第一个元素的索引。否则,返回-1
数组中通过提供测试函数的
第一个元素的索引。否则,返回-1
var inventory = [
{ name: 'apples', quantity: 2 },
{ name: 'bananas', quantity: 0 },
{ name: 'cherries', quantity: 5 }
];
var res = inventory.findIndex(el => {
console.log('findIndex', el);
return el.name === 'bananas'
})
console.log('res', res); // 1
数组排序
sort
let cart = [
{name: 'iphone', price: 12000},
{name: 'imac', price: 18000},
{name: 'ipad', price: 3000},
]
cart.sort(function(a, b){
return a.price - b.price
})
console.log(cart);
sort原理
let arr = [1, 5, 3, 9, 7]
function sort(array, cb){
for (const n in array) {
for (const m in array) {
if(cb(array[n], array[m]) < 0){
const temp = array[n]
array[n] = array[m]
array[m] = temp
}
}
}
return array
}
arr = sort(arr, function(a,b){
return b - a
})
console.log(arr); // [9, 7, 5, 3, 1]
循环操作中引用类型使用技巧
for of
官网说明:
for...of语句在可迭代对象(包括Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句
var arr = [
{name:'苹果', price: 12},
{name:'栗子', price: 10},
{name:'板栗', price: 12}
]
var brr = [1, 2, 3]
for (let i = 0; i < arr.length;i++){
brr[i] = i + 2
}
console.log(brr); // [2, 3, 4]
// for of的复杂数据的内容可以更改
for (const value of arr) {
value.name = `当季${value.name}`
}
console.log('arr', arr);
// for of 基本数据类型无法更改
for (let value of brr) {
value = `数字${value}`
}
console.log('brr', brr); // [2, 3, 4]
var arr = [
{name:'苹果', price: 12},
{name:'栗子', price: 10},
{name:'板栗', price: 12}
]
var brr = [1, 2, 3]
var crr = {
name: 'sss',
age: 12
}
for (const key in arr) {
console.log('key--arr', key); // 索引
}
for (const key in brr) {
console.log('key-brr', key); // 索引
}
for (const key in crr) {
console.log('key-crr', key); // 键值
}
forEach
var arr = [1, 3, 4]
var brr = [
{
num: 1
},
{
num: 3
},
{
num: 4
}
]
arr.forEach(item => {
item = '1' + item
})
console.log(arr);
brr.forEach(item => {
item.num = '1' + item.num
})
console.log(brr);
循环的数据只可以修改引用型数据的内容
iterator迭代器方法玩转数组
let arr = [1,2,3]
let keys = arr.keys()
console.log(keys); // Array Iterator {}
数组.keys()
let arr = ['study', 'everyday']
let keys = arr.keys()
console.log(keys); // Array Iterator {}
console.log(keys.next());
/**
* { value: 0, done: false}
* value: 索引
* done: 是否迭代完成
*/
console.log(keys.next());
/**
* { value: 1, done: false}
* value: 索引
* done: 是否迭代完成
*/
console.log(keys.next());
/**
* { value: undefined, done: true}
* value: 索引
* done: 是否迭代完成
*/
next()取值
数组.values()
let arr = ['study', 'everyday']
let values = arr.values()
console.log(values); // Array Iterator {}
console.log(values.next());
/**
* { value: study, done: false}
* value: 值
* done: 是否迭代完成
*/
console.log(values.next());
/**
* { value: 1, done: false}
* value: 值
* done: 是否迭代完成
*/
console.log(values.next());
/**
* { value: undefined, done: true}
* value: 值
* done: 是否迭代完成
*/
let arr = ['study', 'everyday']
let keys = arr.values()
// 方法一
while (({ value, done} = value.next()) && done === false) {
console.log(value);
}
// 方法二
for (const value of values) {
console.log(value);
}
数组.entries()
let arr = ['study', 'everyday']
let entries = arr.entries()
console.log(entries.next());
let arr = ['study', 'everyday']
let entries = arr.entries()
console.log(entries.next());
// let { done, value } = entries.next()
// console.log(done, value);
// 解构
let [a, b] = arr
console.log('a: ', a, 'b: ', b);
// 解构数据
let { done, value: [index, val] } = entries.next()
console.log(done, index, val);
let arr = ['study', 'everyday']
let entries = arr.entries()
for (const [index, value] of entries) {
console.log(index, value);
}
高效处理数组的方法
some
some()方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。 注意: 如果用一个空数组进行测试,在任何情况下它返回的都是false。
let arr = ['ss', 'ww']
let res = arr.some(function(value, index, arr) {
console.log(value);
return false
})
console.log(res);
<body>
<input type="text" name="title">
<span></span>
<script>
let arr = ['php', 'js']
let inputBox = document.querySelector('[name="title"]')
inputBox.addEventListener("keyup", function(){
const res = arr.some(key => {
return this.value.indexOf(key) !== -1
})
document.querySelector('span').innerHTML = res ? '' : document.querySelector('span').innerHTML = `必须包含${arr.join(',')}关键词`
})
</script>
</body>
every
every()方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。
注意:若收到一个空数组,此方法在一切情况下都会返回 true。
let arr = ['sss', 'nice']
let status = arr.every(function(value, index, arr) {
console.log(index);
return true
})
console.log('status', status); // true
为真,循环继续,直至为false或者循环所有元素
let arr = ['sss', 'nice']
let status = arr.every(function(value, index, arr) {
console.log(index);
return false
})
console.log('status', status); // false
循环为false,就停止循环
let user = [
{name:'张三', score: 89},
{name:'李四', score: 99},
{name:'王五', score: 55},
]
const res = user.every(function(item){
return item.score >= 60
})
res ? console.log('全部同学都及格') : console.log('有同学没有及格');
过滤filter
filter()方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
let arr = [
{name:'张三', score: 33},
{name:'李四', score: 88},
{name:'王五', score: 60}
]
const res = arr.filter(function(value, index, arr){
console.log(value);
return value.score >= 60
})
console.log(res);
自定义过滤函数理解原理
let arr = [1, 2, 3, 4]
function filter(array, except) {
let newArr = []
for (const value of arr) {
if(except.includes(value) === false) {
newArr.push(value)
}
}
return newArr
}
console.log(filter(arr, [2, 3])); // [1, 4]
let arr = [1, 2, 3, 4]
function filter(array, cb) {
let newArr = []
for (const value of arr) {
if(cb(value) === true) {
newArr.push(value)
}
}
return newArr
}
console.log(filter(arr, function(value){
return value > 2
})); // [3, 4]
map映射数组和应用类型处理技巧
let arr = [12, 66, 99]
let newArr = arr.map(function(value, index, arr) {
value = `学生数据-${value}`
return value
})
console.log('arr', arr);
console.log('newArr', newArr);
let arr = [
{name:'张三', score: 33},
{name:'李四', score: 88},
{name:'王五', score: 60}
]
arr.map(function(value, index, arr) {
value.isPassed = true // 改变原数组
})
let arr = [
{name:'张三', score: 33},
{name:'李四', score: 88},
{name:'王五', score: 60}
]
let newArr = arr.map(function(value, index, arr) {
// 不影响原数组
// return Object.assign({isPassed: true}, value)
return {
name: value.name,
score: value.score,
isPassed: true
}
})
console.log('arr', arr);
console.log('newArr', newArr);
Object.assign()
Object.assign(target, ...sources)
参数
target: 目标对象。sources: 源对象。 返回值: 目标对象
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }
reduce
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
-
callback -
执行数组中每个值 (如果没有提供
initialValue则第一个值除外)的函数,包含四个参数: -
accumulator-
累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或
initialValue(见于下方)。 -
currentValue数组中正在处理的元素。
-
index可选数组中正在处理的当前元素的索引。 如果提供了
initialValue,则起始索引号为0,否则从索引1起始。 -
array可选调用
reduce()的数组
-
-
initialValue可选 -
作为第一次调用
callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错
let arr = [1,2,3,4,5]
arr.reduce(function(pre, value, index, arr){
console.log(pre, value);
})
let arr = [1,2,3,4,5]
arr.reduce(function(pre, value, index, arr){
console.log(pre, value);
return 99
})
let arr = [1,2,3,4,5]
arr.reduce(function(pre, value, index, arr){
console.log(pre, value);
return 99
})
arr.reduce(function(pre, value, index, arr){
console.warn(pre, value);
return 99
}, 0)
重复出现的次数
let arr = [1,2,3,1,1]
function arrayCount(array, item){
return arr.reduce(function(total,value){
total += item == value ? 1 : 0
return total
},0)
}
console.log(arrayCount(arr, 1)); // 3
最大值
let arr = [1,9,3,5,4]
function arrayMax(array) {
return arr.reduce(function(pre, cur){
return pre > cur ? pre : cur
})
}
console.log(arrayMax(arr));
// 获取价格超过1万元商品的名称
let cart = [
{name: 'iphone', price: 12000},
{name: 'imac', price: 25000},
{name: 'ipad', price: 3600}
]
function getNames (arr, price){
return arr.reduce(function(pre, cur){
cur.price > price ? pre.push(cur.name) : pre
return pre
}, [])
}
console.log(getNames(cart, 10000));
数组去重
let arr = [1, 2,1,3,4,5,4,3]
let newArr = arr.reduce(function(array, cur) {
if (!array.includes(cur)){
array.push(cur)
}
return array
}, [])
console.log(newArr); // [1,2,3,4,5]
let arr = [
{name: 'iphone', price: 12000},
{name: 'imac', price: 25000},
{name: 'ipad', price: 3600},
{name: 'iphone', price: 12000},
{name: 'imac', price: 25000},
{name: 'ipad', price: 3600}
]
function filterGood(goods){
return goods.reduce(function(array, cur) {
let find = array.find(function(v){
return v.name === cur.name
})
if(!find) array.push(cur)
return array
}, [])
}
console.log(filterGood(arr));
Symbol
声明Symbol
Symbol(描述)
不是全局创建
let study = Symbol('学习') // '学习'是描述
let hard = Symbol('努力')
console.log(study == hard); // false
console.log(study.description);
description
获取描述
Symbol.for(描述)
全局保存,系统会记录
let day = Symbol.for('date') // 系统会记录
let tian = Symbol.for('date')
let ri = Symbol.for('date1')
console.log(day == tian); // true
console.log(day == ri); // false
console.log(Symbol.keyFor(day)); // date
let hard = Symbol('努力')
console.log(Symbol.keyFor(hard)) // undefined
keyFor
使用Symbol.for定义的获取描述。普通定义无法获取
symbol解决字符串耦合问题
symbol在缓存容器中的使用
class Cache {
static data = {}
static set(name, value) {
return (this.data[name] = value)
}
static get(name) {
console.log('name', this.data);
return this.data[name]
}
}
Cache.set('sss', '123')
let user = {
name: 'apple',
description: '用户资料'
}
let cart = {
name: 'apple',
description: '购物车'
}
// Cache.set('apple', user)
// Cache.set('apple', cart)
// 上面直接写会覆盖
Cache.set('user-apple', user)
Cache.set('cart-apple', cart)
console.log(Cache.get('apple'));
class Cache {
static data = {}
static set(name, value) {
return (this.data[name] = value)
}
static get(name) {
console.log('name', this.data);
return this.data[name]
}
}
Cache.set('sss', '123')
let user = {
name: 'apple',
description: '用户资料',
key: Symbol('资料')
}
let cart = {
name: 'apple',
description: '购物车',
key: Symbol('购物车')
}
// Cache.set('apple', user)
// Cache.set('apple', cart)
// 上面直接写会覆盖
Cache.set(user.key, user)
Cache.set(cart.key, cart)
console.log(Cache.get(user.key));
扩展属性与对象属性保护
let symbol = Symbol('这是一个Symbol类型')
let hd = {
name: '素素',
[symbol]: 'susu'
}
Object.keys
// for...in,for...of无法遍历到symbol
for(const key in hd) {
console.log('in', key); // name
}
for(const key of Object.keys(hd)) {
console.log('of', key); // name
}
Object.getOwnPropertySymbols
// 遍历对象中的symbol属性
for (const key of Object.getOwnPropertySymbols(hd)) {
console.log('Object.getOwnPropertySymbols', key); // Symbol(这是一个Symbol类型)
}
Reflect.ownKeys
// 遍历所有的属性
for (const key of Reflect.ownKeys(hd)) {
console.log('Reflect.ownKeys', key); // name Symbol(这是一个Symbol类型)
}
let site = Symbol('这是一个Symbol')
class User {
constructor(name) {
this.name = name
this[site] = 'study.com'
}
getName(){
return `${this[site]} ${this.name}`
}
}
let user = new User('lisi')
console.log(user.getName());
for (const key in user) {
console.log(key); // name
}
Set
Set中的元素只会出现一次,即 Set 中的元素是唯一的。 Set-MDN
Set.add(value)
在Set对象尾部添加一个元素。返回该Set对象
let set = new Set()
set.add(1)
set.add(1)
set.add('1')
console.log(set);
不能有重复的值,但是可以添加不同类型的值
let set = new Set([1,1,2,3,4])
console.log(set); // Set(4) {1, 2, 3, 4}
let obj = {
1: 'susu',
"1": 'SS'
}
console.log(obj); // {"1": 'SS'}
let ds = {
[obj]: '东酥'
}
// 对象做KEY,系统会将其转换为字符串
console.log(ds.toString()); // [object Object]
console.log(ds['[object Object]']); // 东酥
console.log(ds[obj.toString()]); // 东酥
Set.clear()
移除
Set对象内的所有元素。
Set.delete(value)
移除
Set中与这个值相等的元素,返回Set.prototype.has(value)在这个操作前会返回的值(即如果该元素存在,返回true,否则返回false)。Set.prototype.has(value)在此后会返回false。
Set.values()
返回一个新的迭代器对象,该对象包含
Set对象中的按插入顺序排列的所有元素的值。
let set = new Set('dongsu', 'well')
let set1 = new Set(['dongsu', 'well'])
let set2 = new Set()
set2.add(1)
set2.add(2)
console.log(set); // Set(6) {'d', 'o', 'n', 'g', 's', 'u'}
console.log(set1); // Set(2) {'dongsu', 'well'}
console.log(set2); // Set(2) {1, 2}
console.log(set2 instanceof Object); // true
console.log('clear', set.clear()) // undefined
console.log('delete', set1.delete('well')); // true
console.log(set); // Set(1) {size: 0}
console.log(set1); // Set(1) {'dongsu'}
console.log(set1.values()); // SetIterator {'dongsu'}
Set转数组
Array.from
let set = new Set(['dongsu', '东酥'])
let arr = Array.from(set)
console.log(arr); // ['dongsu', '东酥']
...
let brr = [...set]
console.log(brr); // ['dongsu', '东酥']
数组去重
let crr = [1,2,1,2,3,4,5]
crr = [...new Set(crr)]
console.log(crr); // [1, 2, 3, 4, 5]
遍历Set类型的方式
let set = new Set(['dongsu', '东酥'])
console.log(set.values()); // 获取所有元素
console.log(set.keys()); // key === value
console.log(set.entries()); // key === value, {key: value}
- forEach
- for...of
用set的小应用
let obj = {
data: new Set(),
set keyword(word) {
return this.data.add(word)
},
// keyword1(word) {
// return this.data.add(word)
// },
show(){
let ul = document.querySelector('ul')
ul.innerHTML = "";
this.data.forEach(function(value){
ul.innerHTML += `<li>${value}</li>`
})
}
}
let input = document.querySelector("[name='dongsu']")
console.log(input);
input.addEventListener('blur', function(){
obj.keyword = this.value
// obj.keyword1(this.value)
obj.show()
})
并集、交集、差集算法实现
let a = new Set([1,2,3,4,5])
let b = new Set([4,5,6,7])
// 并集
console.log(new Set([...a, ...b]));
// 差集
console.log(
new Set([...a].filter(function(item){
return !b.has(item)
}))
);
// 交集
console.log(
new Set([...a].filter(function(item){
return b.has(item)
}))
);
WeakSet
与Set类似,但是必须是引用类型
let set = new WeakSet('dongsu') // 不是引用类型,报错
let set = new WeakSet(['dongsu']) // 本质是一个字符串,不是引用类型,报错
let set = new WeakSet()
set.add(['dongsu', 'good]) // 添加的类型是数组(引用类型)
- add
- delete
- has
<div>123</div>
<div>456</div>
<script>
let nodes = new WeakSet()
let divs = document.querySelectorAll('div')
divs.forEach(item => {
nodes.add(item)
})
nodes.delete(divs[0])
console.log(nodes.has(divs[0])); // false
console.log(nodes);
</script>
不会有重复的值
WeakSet的弱引用特性
let hd = { name:'后盾人' }
let edu = ds
WeakSet不会让引用计数器+1,这种特性为弱引用
let hd = { name:'dongsu' }
let edu = hd
let set = new WeakSet()
set.add(hd)
hd = null
edu = null
console.log(set);
弱引用的影响:循环的时候,上面有符号,但是本质没有值。但是系统将WeakSet所有的循环方法不能使用
WeakSet保存对象数据,由于是弱引用类型迭代方法用不了
引用数据类型的垃圾回收原理
// 基本数据类型
let a = 1
let b = a
a = null
console.log(b); // 1
// 引用数据类型
let obj = { name: 'dongsu' }
let newObj = obj
obj = null
console.log(newObj); // { name: 'dongsu' }
Map
Map对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。
把对象、函数、标准类型等作为键名
对象当中的键只能是字符串
Map类型增删改查操作
Map.set()
set()方法为Map对象添加或更新一个指定了键(key)和值(value)的(新)键值对
let map = new Map()
map.set('name', 'dongsu')
map.set(1, 'daxie')
console.log(map);
let map1 = new Map([['name','dongsu'],['sex','girl']])
// 链式操作
map1.set('age', 1).set('hobby', 'swim')
// 清晰明了为主
console.log(map1);
Map.get()
返回某个Map对象中的一个指定元素
let map = new Map()
map.set('name', 'dongsu')
console.log(map.get('name')); // dongsu
let obj = {
'age': 1
}
map.set(obj, 'sudong')
console.log(map.get(obj)); // sudong
Map.delete
用于移除Map对象中指定的元素
let map = new Map()
map.set('name', 'dongsu')
let obj = {
'age': 1
}
map.set(obj, 'sudong')
console.log(map.get(obj)); // sudong
map.delete(obj)
console.log('map', map); // map Map(1) {'name' => 'dongsu'}
Map.has
返回一个boolean值,用来表明map中是否存在指定元素
let map = new Map()
map.set('name', 'dongsu')
let obj = {
'age': 1
}
map.set(obj, 'sudong')
console.log(map.get(obj)); // sudong
map.delete(obj)
console.log('map', map);
console.log(map.has(obj)); // false
console.log(map.has('name')); // true
遍历Map类型数据
let map = new Map([['name','dongsu'],['age',16],['sex','boy']])
console.log(map.keys());
console.log(map.values());
console.log(map.entries());
let map = new Map([['name','dongsu'],['age',16],['sex','boy']])
for(const key of map){
console.log(key);
}
for(const key of map.keys()){
console.log(key);
}
for(const value of map.values()){
console.log(value);
}
for(const [key, value] of map.entries()){
console.log(key, value);
}
Map类型转换操作
let hd = new Map([['name', 'ds'], ['age', '18']])
console.log([...hd]);
console.log([...hd.keys()]);
console.log([...hd.values()]);
console.log([...hd.entries()]);
for (const [name, key] of hd) {
console.log('123', name, key);
}
let newArr = [...hd].filter(item => {
console.log('new', item[1]);
return item[1].includes('ds')
})
console.log('newArr', newArr);
let per = new Map(newArr)
console.log(...per.values());
Map类型管理DOM节点
<div>dongsu.com</div>
<div>dscms.com</div>
<script>
// map存储Dom对象
let map = new Map()
document.querySelectorAll('div').forEach(item => {
map.set(item, {
content: item.getAttribute('name')
})
})
map.forEach((config, elem) => {
console.log(elem);
elem.addEventListener("click", ()=>{
console.log(config.content);
})
})
</script>
Map在勾选协议情景下的小练习
<form action="https://baidu.com" onsubmit="return post()">
接受协议:
<input type="checkbox" name="agreement" error="请接收协议">
我是学生:
<input type="checkbox" name="student" error="网站只对学生开放">
<input type="submit">
</form>
<script>
function post() {
let map = new Map()
let inputs = document.querySelectorAll('[error]')
inputs.forEach(item => {
map.set(item, {
item: item.getAttribute('error'),
itemStatus: item.checked
})
});
return [...map].every(([item, config]) => {
console.log(config);
config.itemStatus || alert(config.item)
return config.itemStatus
})
}
</script>
WeakMap的使用
WeakMap弱引用类型
它的键必须是一个对象
let map = new WeakMap()
map.set('name', 'dongsu')
<div>123</div>
<div>344</div>
<script>
let divs = document.querySelectorAll('div')
let map = new WeakMap()
divs.forEach(item => {
map.set(item, item.innerHTML)
})
</script>
set、delete、has
let arr = []
let map = new WeakMap()
map.set(arr, 'dongsu')
map.delete(arr)
console.log('map', map);
console.log('has', map.has(arr));
console.log('keys', map.keys());
keys、for...of不能用
WeakMap弱引用
// 引用类型引用一次,计数器加一,释放则减1
let ds= {name: 'dongsu'}
cms = ds
// 弱类型引用,计算器无变化
let map = new WeakMap()
map.set(ds, 'dongsu.com')
ds = null
cms = null
console.log(map);
函数
函数是对象,JS中函数也是对象函数是
Function类的创建的实例
Function类
// new Function(参数,函数体)
let func = new Function('title', "console.log(title)")
func('东酥')
标准语法
// 字面量创建
function ds(title){ // 具名函数
console.log(title);
}
ds('dongsu')
对象字面量属性函数
let user = {
name: 'dongsu',
getName: function() {
return this.name
},
// 简写
setName(value) {
this.name = value
}
}
全局函数
函数尽量不要独立存放,使用类/模块
全局独立的函数如果命名与系统函数相同,会覆盖系统函数,导致没法使用
匿名函数
let cms = function(title){
console.log(title);
}
cms('dongsu')
show() // 可以正常执行,是因为函数提升
function show() {
console.log('dongsu');
}
show() // 打印dongsu,因为函数存在提升,而匿名函数不会
function show() {
console.log('dongsu');
}
var show = function() {
console.log('dongsu.com');
}
var show = function() {
console.log('dongsu.com');
}
function show() {
console.log('dongsu');
}
show() // dongsu.com
立即执行函数与块作用域解决冲突
- 立即执行函数
// js1.js
(function(window) {
function ds() {
console.log('ds-----js1');
}
function show() {
console.log('show-----js1');
}
window.js1 = { ds, show }
})(window)
- 块作用域
{
let ds = function () {
console.log('ds-----js1');
}
let show = function() {
console.log('show-----js1');
}
window.js1 = { ds, show}
}
<script src="./js1.js"></script>
<script>
/*
引用别人的插件,可能存在命名冲突的问题
*/
// 立即执行函数
function ds(){
console.log(123);
}
js1.ds()
</script>
推荐使用模块化的方式
形参和实参
function sum(a, b) {
return a + b
}
console.log(sum(1, 3));
默认参数的使用
默认参数放在最后
function avg(total, year = 1) {
return Math.round(total / year)
}
console.log(avg(2000, 2));
function sortArray(array, type = 'asc') {
return array.sort(function (a, b) {
return type === 'asc' ? a - b : b - a
})
}
console.log(sortArray([3, 1, 7, 2]));
函数参数
函数的参数不受类型的约束
var i = 0;
function cms() {
console.log(++i);
}
setInterval(cms, 1000)
arguments
function sum(){
console.log(arguments);
console.log(arguments.length);
// arguments.reduce不能用,因为arguments是伪数组
return [...arguments].reduce((a, b) => a + b)
}
console.log(sum(1,4,5,6,7,88));
箭头函数
let dongsu = function() {
return 'dong' + 'su'
}
console.log(dongsu());
let ds = () => 'dong' + 'su'
console.log(ds());
不适用箭头函数:
- 递归函数
- 构造函数
- 事件处理
使用函数实现递归算法
function factorial(num) {
return num == 1 ? num : num * factorial(num - 1)
}
console.log(factorial(5));
function sum(...args){
return args.length === 0 ? 0 : args.pop() + sum(...args)
}
console.log(sum(1,3,2,44));
递归实现倒三角
function star(num){
return num ? document.write('*'.repeat(num) + '<br />') || star(--num) : ''
}
star(5)
回调函数
简要理解:在其他函数中调用的函数
let ds = [1, 2, 3, 4]
ds.map(function(value, index, array){
array[index] += 10
return value
})
console.table(ds)
展开语法(点语法)
let ds = [1, 2, 3, 4]
let [a, b, c] = [...ds]
console.log(a, b, c); // 1,2,3
console.log([...ds]); // [1,2,3,4]
let [g, ...num] = [1, 2, 3, 4]
console.log(num); // [2,3,4]
function sum(num, ...args){
console.log(args); // [2,3,4]
}
console.log(sum(1,2,3,4));
展开语法放在最后
函数和方法中this的不同
let ds = {
name: 'dongsu',
show: function () {
console.log(this.name); // this当前的对象
console.log(this);
function render() {
console.log(this); // window
}
render() // window.render()
}
}
ds.show()
// this 指向引用的环境
通过常量改变this指针
let lesson = {
site: '东苏',
lists: ['js', 'css', 'vue', 'react'],
show: function(){
const self = this
return this.lists.map(function(value){
return `${self.site}-${value}`
})
}
}
console.log(lesson.show());
let lesson = {
site: '东苏',
lists: ['js', 'css', 'vue', 'react'],
show: function(){
return this.lists.map(function(value){
return `${this.site}-${value}`
}, this)
}
}
console.log(lesson.show());
箭头函数中的this
<button>search</button>
<script>
let ds = function () {
console.log('东酥');
}
ds()
// 箭头函数的this指向上下文(父级作用当中的this)
// 普通函数的this指向window
// 如果函数是一个属性的值,那么this是这个对象
let Dom = {
site: '东酥',
bind: function () {
console.log(this);
const button = document.querySelector('button')
// button.addEventListener('click', () => {
// console.log(this); // {site:'东酥', bind: f}
// })
button.addEventListener('click', function () {
console.log(this); // button
})
// =======>
// button.click = function() {
// console.log('this', this);
// }
}
}
Dom.bind()
</script>
this构造原理实现
call
改变this指向,立即执行
function User(name){
console.log(this); // User {}
this.name = name
}
let lisi = new User('李四')
console.log(lisi);
let hscms = { url: 'dongsu.com' }
User.call(hscms, '123') // User的this指向第一个参数hscms,第二个参数作为User的形参
console.log(hscms);
apply
改变this指向,立即执行
let zhangsan = {
name: '张三'
}
let wangwu = {
name: '王五'
}
function User(web, url) {
console.log(this);
console.log(web + url + this.name);
}
User.call(zhangsan, 'dongsu', 'dongsu.com'); // 改变this指向,立即执行
User.apply(zhangsan, ['dongsu', 'dongsu.com']); // 改变this指向,立即执行
apply与call的区别就是参数的格式
// 求最大值
console.log(Math.max(12, 3, 4, 55));
let arr = [1, 3, 6, 2]
console.log(Math.max(...arr));
console.log(Math.max.apply(Math, arr));
Math.max.apply(Math, arr),这里不能使用call,call会把参数当成一个整体(会把数组直接传递,Math.max会报NaN)
收缩折叠面板的应用
<dl>
<dt>东酥</dd>
<dd>1</dd>
<dt>dongsu</dd>
<dd hidden="hidden">2</dd>
</dl>
<script>
function panel(index) {
var dds = document.querySelectorAll('dd')
dds.forEach(dd => {
dd.setAttribute('hidden', 'hidden')
})
dds[index].removeAttribute('hidden')
}
var dts = document.querySelectorAll('dt')
dts.forEach((dt, index) => {
dt.addEventListener('click', () => {
// panel(index)
panel.call(null, index)
})
})
</script>
<style>
* {
padding: 0;
margin: 0;
}
dt {
background-color: orange;
text-align: center;
width: 200px;
}
dd {
background-color: #eee;
height: 200px;
text-align: center;
width: 200px;
}
.hidden {
display: none;
}
</style>
call不一定要改变this,可以传null
bind
bind不会立即执行,使用bind改变this指向,它会得到新的函数
function show() {
console.log(this.name);
}
show.apply({ name: 'ss' })
// bind不会立即执行,使用bind改变this指向,它会得到新的函数
show.bind({ name: 'dong' })()
let a = function () {}
let b = a
console.log(a instanceof Object); // true
console.log(a === b); // true
b = a.bind()
console.log(a === b); // false
bind参数
// 参数传递
function ds(a,b){
return this.f + a + b
}
// console.log(ds.call({f:1}, 1, 1)); // 3
let funF = ds.bind({f: 1}, 2, 2) // 先从bind第二个参数查找是否有参数,如果有直接使用,如果没有就去新函数的参数中查找
console.log(funF(2,4)); // 如果bind的参数没有,就在新产生的函数中查找参数
bind应用
<button>好好学习</button>
<script>
document.querySelector('button').addEventListener('click', function(event) {
document.write(this.url + event.target.innerHTML)
}.bind({url: 'dongsu.com'}))
</script>
addEventListener的function不是立即执行
function Color(elem) {
this.elem = elem
this.colors = ['red', 'blue', 'pink']
this.run = function(){
// setInterval(() => {
// console.log(this);
// let i = Math.floor(Math.random() * this.colors.length)
// console.log(this.elem);
// this.elem.style.background = this.colors[i]
// }, 1000);
setInterval(function(){
console.log(this);
let i = Math.floor(Math.random() * this.colors.length)
console.log(this.elem);
this.elem.style.background = this.colors[i]
}.bind(this), 1000);
}
}
let obj = new Color(document.body)
obj.run()
环境与作用域
- 全局环境 定义的数据会被保留、不会被卸载、不会被回收 除非关闭浏览器、人为去操作
函数每调用一次,就产生一个内存空间,调用完成就清除内存
数据在被使用,就不会被清除掉
function hd(){
let n = 1;
return function sum() {
console.log(n++);
}
}
let a = hd()
a()
function DS(){
let n = 1;
this.sum = function(){
console.log(++n);
}
}
let a = new DS()
a.sum() // 2
a.sum() // 3
函数在被使用,同作用域下的内容也被保留
var、let、const
for(var i = 1; i <= 3; i++){
console.log(i);
}
console.log(i); // window.i
for(let i = 1; i <= 3; i++){
console.log(i);
}
console.log(i); // window.i
for(var i = 1; i <= 3; i++){
setTimeout(() => {
console.log(i);
}, 1000);
}
console.log(i); // window.i
for(let i = 1; i <= 3; i++){
setTimeout(() => {
console.log(i);
}, 1000);
}
console.log(i); // window.i
for (var i = 1; i <= 3; i++) {
(function (a) {
setTimeout(() => {
console.log(a);
}, 1000);
})(i)
}
console.log(i); // window.i
let arr = []
for (var i = 1; i <= 3; i++) {
arr.push(function(){
return i;
})
}
console.log(arr[0]()); // 4
console.log(arr[1]()); // 4
console.log(arr[2]()); // 4
let arr = []
for (var i = 1; i <= 3; i++) {
(function (i) {
arr.push(function () {
return i;
})
})(i)
}
console.log(arr[0]()); // 1
console.log(arr[1]()); // 2
console.log(arr[2]()); // 3
let arr = []
for (let i = 1; i <= 3; i++) {
arr.push(function(){
return i;
})
}
console.log(arr[0]()); // 1
console.log(arr[1]()); // 2
console.log(arr[2]()); // 3
闭包
函数能访问到其他函数作用域当中的数据
function ds (){
let n = 1;
return function sum(){
console.log(++n);
}
}
let a = ds()
a() // 2
a() // 3
闭包的内存泄露解决方法
<div desc="online">在线学习</div>
<div desc="progress">不断进步</div>
<script>
let divs = document.querySelectorAll('div')
divs.forEach(function(item){
item.addEventListener('click', function(){
console.log(item.getAttribute('desc'));
})
})
</script>
上面存在的问题,点击只是为了获得desc属性的值,而这里直接使用item,使得整个DOM都保存在内存当中,导致内存空间的占用
<div desc="online">在线学习</div>
<div desc="progress">不断进步</div>
<script>
let divs = document.querySelectorAll('div')
divs.forEach(function(item){
let desc = item.getAttribute('desc')
item.addEventListener('click', function(){
// console.log(item.getAttribute('desc'));
console.log(desc);
console.log(item);
})
item = null
})
</script>
this在闭包中的历史遗留问题
let hd = {
user: '后盾人',
get: function(){
return function() {
return this.user // this指向window
}
}
}
let a = hd.get()
console.log(a()); // undefined 外部调用指向window
let hd = {
user: '后盾人',
get: function(){
return () => {
return this.user // this指向hd
}
}
}
let a = hd.get()
console.log(a()); // 后盾人
对象
函数变成与面向对象的实例对比
传统的函数编程会有错中复杂的依赖很容易创造意大利式面条代码
面条式代码:是非结构化和难以维护的源代码的贬义词组,广泛地解释。 意大利面条代码可能由多种因素引起,例如易变的项目要求,缺乏编程风格规则以及能力或经验不足
属性的基本操作
- 获取 属性是变量变量、属性中特殊的符号(如空格)
对象[属性名]
- 设置
对象.属性名
- 删除
delete 对象.属性名/对象[属性名]
let user = {
name: '东酥',
"my age": 18
}
console.log(user["my age"]); // 18
user.get = function(){
return `${this.name}的年龄是${this['my age']}`
}
console.log(user.get());
对象是引用类型
let user = { name: '东酥'}
function show(user) {
user.age = 18 // 传的是复杂类型的数据的地址,所以当修改时会直接修改复杂数据,所有引用的地方都会被修改
console.log(user);
}
show(user)
console.log(user); // {name: '东酥', age: 18}
使用展开语法完成参数合并
let user = { name:'东酥', age: 22}
let ds = { ...user, hobby: 'eating' }
console.log(ds); // {name: '东酥', age: 22, hobby: 'eating'}
解构
解构赋值新增特性
对结构的分解处理
let user = { name: '东酥', age: 18 }
let { name: a, age } = user
console.log('name:', a,'age:', age); // name: 东酥 age: 18
严格模式下解构的差异
"use strict"
let user = { name: '东酥', age: 18 };
({ name: a, age } = user)
console.log('name:', a,'age:', age);
"use strict"
let user = { name: '东酥', age: 18 };
({ name: a, age } = user)
console.log('name:', a,'age:', age);
解构操作的简写形式与变量解构
/*
let {原属性名:新属性名} = 对象
如果原属性名与新属性名一致,就可以简写为
let {属性名} = 对象
*/
let user = { name: '东酥', age: 18 }
let { name, age } = user
console.log(name, age);
解构默认值实现配置下合并
值存在就不使用默认值,不存在则使用默认值
let arr = ['东酥', 'dongsu']
let [a, b, c = '我是默认值'] = arr
console.log(a, b, c); // 东酥 dongsu 我是默认值
let arr = ['东酥', 'dongsu', 'ccc']
let [a, b, c = '我是默认值'] = arr
console.log(a, b, c); // 东酥 dongsu ccc
函数参数中使用解构
function ds(name, { sex, age }) {
console.log(name, sex, age); // 东酥 男 18
}
ds('东酥', { sex: '男', age: 18 })
function ds(name, { sex: DSex, age: Dage }) {
console.log(name, DSex, Dage); // 东酥 男 18
}
ds('东酥', { sex: '男', age: 18 })
对象属性的添加删除操作
检测属性:hasOwnProperty
hasOwnProperty检测对象自身是否包含指定的属性,不检测原型链上继承的属性
/**
* hasOwnProperty: 判断当前对象上有没有属性
*/
let ds = {}
ds.name = '东酥'
ds['age'] = 18
console.log(ds);
delete ds.name
console.log(ds.hasOwnProperty('age')); // true
in 可以在原型对象上检测
let ds = { name: '东酥' }
let arr = ['dongsu', '东酥']
console.log(arr.hasOwnProperty('length')); // true length存在于数组对象当中
console.log(arr.hasOwnProperty('concat')); // false
// in 不仅检测自己,还检测父级
console.log('length' in arr); // true
console.log('concat' in arr); // true
setPrototypeOf
Object.setPrototypeOf(obj, prototype)
方法设置一个指定的对象的原型 ( 即, 内部[[Prototype]]属性)到另一个对象或null
-
obj: 要设置其原型的对象。.
-
prototype: 该对象的新原型(一个对象 或 null).
let a = {
name: '东酥'
}
let b = {
url: 'dongsu.com'
}
Object.setPrototypeOf(a, b)
console.log(a);
console.log(a.hasOwnProperty('url')); // false
console.log('url' in a); // true
计算属性与assign使用
let lessons = [
{
title: 'JS高级内容',
category: 'JS'
},
{
title: 'css样式',
category: 'css'
},
{
title: 'vue深入浅出',
category: 'Vue'
}
]
let res = lessons.reduce((obj, cur, index) => {
obj[`${cur["category"]}-${index}`] = cur
return obj
}, {})
console.log(JSON.stringify(res, null, 2));
assign
let ds = Object.assign({name:'东书'}, {age: 18})
console.log(ds);// {name: '东书', age: 18}
对象的浅拷贝
Object.assign()拷贝
当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。
对象的深拷贝
递归
function copy (obj) {
let res = obj instanceof Array ? [] : {}
for (const [k, v] of Object.entries(obj)) {
res[k] = typeof v == 'object' ? copy(v) : v
}
return res
}
工厂函数创建对象
构造函数创建对象
属性的特性
封闭对象
Object.seal()
封闭之后不能添加属性,不能修改
Object.isSealed()
是否处于封闭状态
冻结属性
Object.freeze()
不能添加、删除、修改、遍历
Object.isFrozen()
是否处于冻结状态
访问器
const user = {
data: { name: '后盾人', age: 10 },
set age(value) {
console.log(value);
if (typeof value != "number" || value < 10 || value > 100) {
throw new Error("年龄格式错误")
}
this.data.age = value
},
get age() {
return this.data.age + '岁'
}
}
user.age = 99
访问器伪造属性操作
let lesson = {
lists: [
{name:'js',price: 100},
{name:'mysql',price: 200},
{name:'vue',price: 300}
],
get total() {
return this.lists.reduce((t,l)=>t + l.price, 0)
}
}
console.log(lesson.total); // 600
lesson.total = 999
console.log(lesson.total); // 600
使用访问器批量设置属性
const web = {
name: '东酥',
url: 'dongsu.com',
get site() {
return `${this.name}的网址是${this.url}`
},
set site(value) {
[this.name, this.url] = value.split(',')
}
}
web.site = "开源产品,www.baidu.com"
console.log(web.site);
let Request = {
set token(content) {
sessionStorage.setItem('token', content)
},
get token() {
let token = sessionStorage.getItem('token')
if (!token) {
alert('请登录')
}
return token
}
}
Request.token = '232194932848128'
console.log(Request.token);
访问器的优先级
const user = {
name: '东酥',
age: 10,
set name(value) {
console.log(value + '-forever');
}
}
user.name = "dongsu"
console.log(user); // {age: 10}
说明访问器的优先级更高
const DATA = Symbol() // 唯一,保护数据
const user = {
[DATA]: { name },
age: 10,
set name(value) {
this[DATA].name = value
},
get name() {
return this[DATA].name
}
}
user.name = 'dongsu'
console.log(user[Symbol()]); // undefined
构造函数与class语法糖中使用访问器
function User(name, age) {
this.name = name;
this.age = age;
}
let ds = new User('东酥', 18)
ds.name = '小傻逼'
上面这种写法,数据可被随意的更改
function User(name, age) {
let data = { name, age }
Object.defineProperties(this, {
name: {
get() {
return data.name
},
set(value) {
if (value.trim() !== '' || value.length > 20){
return new Error('用户名不合法')
}
data.name = value
}
},
age: {
get() {
return data.age
},
set(value) {
data.age = value
}
}
})
}
let ds = new User('东酥', 18)
ds.name = 'fff'
console.log(ds.name); // 东酥
const DATA = Symbol() // 唯一,保护数据不为修改
class User {
constructor(name, age) {
this[DATA] = { name, age }
}
get name() {
return this[DATA].name
}
set name(value) {
if (value.trim() !== '' || value.length > 20) {
return new Error('用户名不合法')
}
this[DATA].name = value
}
get age() {
return this[DATA].age
}
set age(value) {
this[DATA].age = value
}
}
let ds = new User('东酥', 18)
ds.name = 'fff'
console.log(ds.name); // 东酥
console.log(ds);
Proxy代理拦截
- 访问器对单个属性进行控制
- 对象代理是对整个对象进行控制
const ds = { name: '东酥', age: 18 }
const proxy = new Proxy(ds, {
get(obj, property) {
return obj[property]
},
set(obj, property, value) {
obj[property] = value
}
})
console.log(proxy.name);
console.log(proxy.age);
- 代理Proxy控制函数
function factorial(num) {
return num == 1 ? 1 : num * factorial(num - 1)
}
let res = factorial(5)
console.log(res); // 120
function factorial(num) {
return num == 1 ? 1 : num * factorial(num - 1)
}
let proxy = new Proxy(factorial, {
apply(func, obj, args) {
/**
* func 函数
* obj 当前上下文的对象 this
* args 参数
*/
console.time('run')
console.log(func, obj, args);
console.timeEnd('run')
}
})
// proxy.apply(this, [5])
proxy.apply({}, [5])
数组使用代理拦截操作
let lessons = [
{
title: 'css',
type: 'css'
},
{
title: 'Mysql',
type: 'mysql'
},
{
title: 'flex布局',
type: 'css'
},
{
title: 'vue',
type: 'vue.js'
},
]
let proxy = new Proxy(lessons, {
get(arr, key) {
console.log(arr, key);
const title = arr[key].title
const len = 5
arr[key].title = title.length > len ? title.substr(0, len) + '.'.repeat(3) : title
return arr[key]
}
})
// console.log(proxy[0]);
console.log(JSON.stringify(proxy[1], null, 2));
VUEJS数据绑定的容器更新
<input type="text" v-model="title">
<input type="text" v-model="title">
<div v-bind="title">这里也会发生更新</div>
<script>
function View() {
let proxy = new Proxy({}, {
get(obj, property) {},
set(obj, property, value) {
}
})
this.init = function() {
const els = document.querySelectorAll('[v-model]')
els.forEach(item => {
item.addEventListener('keyup', function () {
proxy[this.getAttribute('v-model')] = this.value
})
})
}
}
new View().init()
</script>
双向数据绑定的页面渲染
<input type="text" v-model="content">
<h4 v-bind="content"></h4>
<hr>
<input type="text" v-model="title">
<input type="text" v-model="title">
<div v-bind="title">这里也会发生更新</div>
<script>
function View() {
let proxy = new Proxy({}, {
get(obj, property) { },
set(obj, property, value) {
document.querySelectorAll(`[v-model="${property}"]`).forEach(item => {
item.value = value
})
document.querySelectorAll(`[v-bind="${property}"`).forEach(item => {
item.innerHTML = value
})
}
})
this.init = function () {
const els = document.querySelectorAll('[v-model]')
els.forEach(item => {
item.addEventListener('keyup', function () {
proxy[this.getAttribute('v-model')] = this.value
})
})
}
}
new View().init()
</script>
表单验证组件的代理工厂
<style>
.error {
border: 1px solid red;
}
</style>
<input type="text" validate rule="max: 12,min: 3">
<input type="text" validate rule="max: 3,isNumber">
<script>
// 表单验证体验代理
class Validate {
max(value, len) {
return value.length <= len
}
min(value, len) {
return value.length >= len
}
isNumber(value) {
return /^\d+$/.test(value)
}
}
function ProxyFactory(target) {
return new Proxy(target, {
get(target, key) {
return target[key]
},
set(target, key, el) {
console.log(el);
const rule = el.getAttribute("rule")
const validate = new Validate()
let state = rule.split(",").every(rule => {
const info = rule.split(":")
return validate[info[0]](el.value, info[1])
})
// 根据状态来修改样式
el.classList[state ? 'remove' : 'add']('error')
return true // 严格模式下,要return
}
})
}
const proxy = ProxyFactory(document.querySelectorAll('[validate]'))
proxy.forEach((item, i) => {
console.log(item);
item.addEventListener('keyup', function () {
proxy[i] = this
})
});
</script>
JSON数据
- json 是一种轻量级的数据交换格式,易于人阅读和编写。
- 使用
json数据格式是替换xml的最佳方式,主流语言都很好的支持json格式。所以json也是前后台传输数据的主要格式。 - json 标准中要求使用双引号包裹属性,虽然有些语言不强制,但使用双引号可避免多程序间传输发生错误语言错误的发生
let data = {
name: '东酥',
data: {
title: 'php'
}
}
let json = JSON.stringify(data, null, 2)
console.log(json);
let obj = JSON.parse(json)
console.log('obj', obj);
JSON序列化与自定义toJSON
序列化是将
json转换为字符串,一般用来向其他语言传输使用
let ds = {
name: '东酥',
url: 'www.baidu.com',
teacher: {
name: '向军大叔'
},
// 为数据添加 toJSON 方法来自定义返回格式
toJSON: function() {
return {
name: this.name
}
}
}
/**
* JSON.stringify 转化为JSON格式
* 参数一:需要转为JSON的数据
* 参数二: 指定保存的属性
* 参数三:Tab制表位(处理JSON的展示效果)
*/
let json = JSON.stringify(ds)
console.log(json);
let arr = ['东酥', 'dongsu']
let arrJSON = JSON.stringify(arr, ['name', 'en'], 2)
console.log(arrJSON);
toJSON: 自定义返回格式
JSON转JS
反序列化
使用第二个参数函数来对返回的数据二次处理
let ds = {
name: '东酥',
url: 'www.baidu.com',
teacher: {
name: '向军大叔'
}
}
let json = JSON.stringify(ds, null, 2)
console.log(json);
let obj = JSON.parse(json, (key, value) => {
// console.log('parse', key, value);
if (key == 'name') {
value = '人名' + value
}
return value
})
console.log(obj);
原型
初步认识
let hd = {}
console.log(Object.getPrototypeOf(hd));
let xj = {}
console.log(Object.getPrototypeOf(xj));
console.log(Object.getPrototypeOf(hd) === Object.getPrototypeOf(xj));// true
没有原型的对象
Object.create()
let xj = { name: '向军' }
console.log(xj);
console.log(xj.hasOwnProperty("name"));
/**
* Object.create
* 第一个参数:指定父级(原型)
*/
// 完全数据字典对象
let ds = Object.create(null, {
name: {
value: 'dongsu'
}
})
console.log(ds);
原型方法和对象方法的优先级
let ds = {
show(){
console.log('我本身的方法');
}
}
ds.__proto__.show = function(){
console.log('原型中的方法');
}
console.log(ds.__proto__);
ds.show() // 我本身的方法
函数的原型
- 服务于函数实例化
function User() { }
User.prototype.show = function() {
console.log('show-----');
}
// User.apply
let hd = new User()
hd.show()
console.log(User.prototype === hd.__proto__);
console.dir(User);
- 服务于函数对象
function User() {}
User.__proto__.view = function () {
console.log('user function user methods');
}
User.view()
console.dir(User);
let hd = new Object()
hd.name = '后端人'
// console.dir(Object)
Object.prototype.show = function() {
console.log('houdunren.com');
}
// hd.show()
function User(){}
console.dir(User);
console.log(User.prototype.__proto__ === User.__proto__.__proto__);
// console.dir(Object.prototype.__proto__); // null
let hd = new Object()
hd.name = '后端人'
// console.dir(Object)
Object.prototype.show = function() {
console.log('houdunren.com');
}
// hd.show()
function User(){}
User.show() // houdunren.com
let hd = new Object()
hd.name = '后端人'
// console.dir(Object)
Object.prototype.show = function() {
console.log('houdunren.com');
}
// hd.show()
function User(){}
let xj = new User()
xj.show() // houdunren.com
系统构造函数的原型体现
let obj = {}
console.log(obj.__proto__ === Object.prototype); // true
let arr = []
console.log(arr.__proto__ === Array.prototype); // true
let str = ''
console.log(str.__proto__ === String.prototype); // true
let bool = true
console.log(bool.__proto__ === Boolean.prototype); // true
let reg = /a/i
console.log(reg.__proto__ === RegExp.prototype); // true
修改构造函数原型上面的方法,后面继承的元素都会受到影响
自定义对象的原型设置
let hd = { name: 'ds' }
let parent = {
name: 'parent',
show() {
console.log('parent', this.name); // this:谁调用就是谁
}
}
console.log(hd.__proto__ === Object.prototype);
Object.setPrototypeOf(hd, parent)
console.log(hd);
hd.show() // ds
parent.show() // parent
console.log(Object.getPrototypeOf(hd));
console.log(Object.getPrototypeOf(parent));
原型中constructor的引用
function User(name) {
this.name = name
}
// 直接给prototype赋值(这种方式可以添加多个内容),会改变原型
// User.prototype = {
// // constructor: User, // 如果直接给prototype赋值,会改变原型,需要指定constructor
// show(){
// console.log(this.name);
// }
// }
User.prototype.show = function(){
console.log(this.name);
}
console.dir(User);
console.log(User.prototype.constructor === User); // true
let lisi = new User.prototype.constructor('李四')
lisi.show() // 李四
根据一个对象再生成一个新的对象
function User(name) {
this.name = name;
this.show = function(){
console.log(this.name);
}
}
User.prototype = {
constructor: User,
show() {
console.log(this.name)
}
}
let ds = new User('东酥')
console.log('ds', ds);
function createByObject(obj, ...args) {
const constructor = Object.getPrototypeOf(obj).constructor
// constructor === User
return new constructor(...args)
}
let Ds = createByObject(ds, '小傻逼')
console.log('Ds', Ds);
Ds.show()
原型链
let arr = [] // new Array
console.log(arr.__proto__);
console.log(Object.getPrototypeOf(arr));
console.log(arr.__proto__.__proto__ === Object.prototype);
console.log(Object.prototype.__proto__); // null
let a = { name: '东酥' }
let b = {
name: 'b',
show() {
console.log(this.name);
}
}
Object.setPrototypeOf(a, b)
a.show()
原型链检查之instanceof
构造函数的prototype是否在另一个对象的原型链上
function A() { }
function B() { }
let b = new B()
A.prototype = b
let a = new A()
// a instanceof A: a的原型链上是否有A.prototype
console.log(a instanceof A);
console.log(a instanceof Object);
console.log(a instanceof B);
function A() { }
function B() { }
function C() { }
let c = new C()
B.prototype = c
let b = new B()
A.prototype = b
let a = new A()
// a instanceof A: a的原型链上是否有A.prototype
console.log(a instanceof A); // true
console.log(a instanceof Object); // true
console.log(a instanceof B); // true
console.log(a instanceof C); // true
console.log(b instanceof A); // false
Object.isPrototypeOf原型检测
一个对象是否在另一个对象的原型链上
// Object.prototype = b.__proto__
let a = {}
let b = {}
console.log(Object.prototype.isPrototypeOf(a)); // true
console.log(b.__proto__.isPrototypeOf(a)); // true
console.log(b.isPrototypeOf(a)); // false
// Object.prototype = b.__proto__
let a = {}
let b = {}
let c = {}
Object.setPrototypeOf(b, c)
Object.setPrototypeOf(a, b)
console.log(Object.prototype.isPrototypeOf(a)); // true
console.log(b.__proto__.isPrototypeOf(a)); // true
console.log(b.isPrototypeOf(a)); // true
console.log(c.isPrototypeOf(b)); // true
console.log(c.isPrototypeOf(a)); // true
in和hasOwnProperty的属性检测
in
检测属性是否在对象或对象的原型链上
let a = { url: 'https://ww.baidu.com' }
let b = { name: '百度' }
Object.prototype.web = 'dongsu'
console.log('url' in a); // true
console.log('web' in a); // true
console.log('name' in a); // false
Object.setPrototypeOf(a, b)
console.log('name' in a); // true
hasOwnProperty
仅仅检测对象本身,不会检测原型链
let a = { url: 'https://ww.baidu.com' }
let b = { name: '百度' }
Object.setPrototypeOf(a, b)
console.log(a.hasOwnProperty('name')); // false
console.log(a.hasOwnProperty('url')); // true
for (const key in a) {
console.log(key);
}
for (const key in a) {
if (Object.hasOwnProperty.call(a, key)) {
const element = a[key];
console.log('a-el', element);
}
}
call和apply借用原型链
apply
let ds = {
data: [1, 3, 5, 6, 67, 7]
}
Object.setPrototypeOf(ds, {
max() {
return this.data.sort((a, b) => b - a)[0]
}
})
console.log(ds.max());
let sd = {
lessons: { js: 88, php: 90, node: 99, linux: 98 },
get data() {
return Object.values(this.lessons)
}
}
console.log(ds.max.apply(sd));
call
let ds = {
data: [1, 3, 5, 6, 67, 7]
}
Object.setPrototypeOf(ds, {
max(data) {
return data.sort((a, b) => b - a)[0]
}
})
console.log(ds.max(ds.data));
let sd = {
lessons: { js: 88, php: 90, node: 99, linux: 98 }
}
console.log(ds.max.call(null, Object.values(sd.lessons)));
优化
/**
* Math.max(1, 3, 5, 6, 67, 7)
* 传递多个参数:使用apply
*/
let ds = {
data: [1, 3, 5, 6, 67, 7]
}
console.log(Math.max.apply(null, ds.data));
let sd = {
lessons: { js: 88, php: 90, node: 99, linux: 98 }
}
console.log(Math.max.apply(null, Object.values(sd.lessons)));
/**
* Math.max(1, 3, 5, 6, 67, 7)
* 传递多个参数:使用apply
* call的参数作为一个整体传递
*/
let ds = {
data: [1, 3, 5, 6, 67, 7]
}
console.log(Math.max.call(null, ...ds.data));
let sd = {
lessons: { js: 88, php: 90, node: 99, linux: 98 }
}
console.log(Math.max.call(null, ...Object.values(sd.lessons)));
DOM节点借用Array原型方法
<button message='东酥' class="red">东酥</button>
<button message='dongsu'>dongsu</button>
<script>
let arr = [1,2,4]
let res = arr.filter(item => {
return item > 39
})
// console.dir(Array.prototype.filter)
let btns = document.querySelectorAll('button')
// DOM数组是伪元素
// btns = [].filter.call(btns, item => {
// return item.hasAttribute('class')
// })
// ===========>
btns = Array.prototype.filter.call(btns, item => {
return item.hasAttribute('class')
})
console.log('btns', btns);
console.log(btns[0].innerHTML);
</script>
合理的构造函数方法声明
function User(name) {
this.name = name;
this.show = function() {
console.log(this.name);
}
}
let lisi = new User('李四')
let ds = new User('东酥')
console.log('lisi', lisi);
console.log('ds', ds);
同一个方法,开辟的两个空间都占用了,内存浪费
function User(name) {
this.name = name;
}
User.prototype.show = function () {
console.log(this.name);
}
let lisi = new User('李四')
let ds = new User('东酥')
console.log('lisi', lisi);
console.log('ds', ds);
设置在原型上面,只占用一份进行复用
function User(name) {
this.name = name;
}
// User.prototype.show = function () {
// console.log(this.name);
// }
// User.prototype.get = function () {
// console.log('get.....');
// }
User.prototype = {
constructor: User,
show(){
console.log(this.name);
},
get(){
console.log('get.....');
}
}
let lisi = new User('李四')
let ds = new User('东酥')
console.log('lisi', lisi);
console.log('ds', ds);
lisi.show()
lisi.get()
this和原型的关系
this和原型没什么关系
let ds = {
name: '东酥'
}
let User = {
name: '后盾人',
show() {
console.log(this.name);
}
}
Object.setPrototypeOf(ds, User)
console.log(ds.show()); // 东酥 this指向调用的对象
不要滥用原型
<button onclick="this.hide()">点击</button>
<script>
Object.prototype.hide = function() {
this.style.display = 'none'
}
</script>
强烈不建议在系统的原型中追加方法,相同命名会被覆盖,导致其非常不稳定
Object.create和__proto__
let user = {
show() {
return this.name;
}
}
// prototype
// 为单个对象更改原型
// 定义对象的原型,不能获取
let ds = Object.create(user, {
name: {
value: '小东东'
}
})
// ds.name = "东酥"
console.log(ds.show());
let user = {
show() {
return this.name;
}
}
// __proto__ 可以设置,也可以获取
let ds = { name: '东酥' }
ds.__proto__ = user
console.log(ds.show());
console.log(ds.__proto__);
使用setPrototypeOf替代__proto__
Object.setPrototypeOf(参数一,参数二)
参数一继承参数二的原型
let user = {
show() {
return this.name;
}
}
let ds = {
name: '东酥'
}
Object.setPrototypeOf(ds, user)
console.log(ds.show());
console.log(Object.getPrototypeOf(ds));
Object.getPrototypeOf(参数一):获取参数一的原型
__proto__是非标准的
__proto__原来是属性访问器
let ds = { name: '东酥'}
ds.__proto__ = {
show() {
console.log(this.name);
}
}
ds.__proto__ = 99
ds.show() // 东酥
console.log(ds.__proto__);
__proto__的setter做了设置
仿写
let ds = {
action: {},
get proto() {
return this.action
},
set proto(obj) {
if (obj instanceof Object) {
this.action = obj
}
}
}
ds.proto = { view: function(){}}
ds.proto = 'abc'
console.log(ds.proto);
非要使用__proto__去修改数据
// 创建没有原型的数据
let ds = Object.create(null)
console.dir(ds)
ds.__proto__ = '东酥'
console.dir(ds.__proto__)
学习笔记