目录
十九、有一个祖先树状 json 对象,当一个人有一个儿子的时候,其 child 为其儿子对象,如果有多个儿子,child 为儿子对象的数组。
一、解析URL
<script>
var url = "https: //zhidao.baidu.com/question/1768422895052400180.html?fr=iks&word=slice&ie=gbk"
function getUrl(url) {
let s = url.split('?')[1]
let str = s.split('&')
let obj = {}
for (let i = 0; i < str.length; i++) {
let key = str[i].split('=')[0]
let value = str[i].split('=')[1]
obj[key] = value
}
return obj
}
console.log(getUrl(url));
</script>
二、功能函数实现
1、防抖和节流
(1)防抖
频繁操作时,只执行最后一次
(2)节流
频繁操作时,只有在一定时间内执行一次
<button>btn</button>
<script>
function test(fn, delay) {
let timer = null
return function() {
if(timer){
return
}
timer = setTimeout(() => {
fn()
timer = null
}, delay)
}
}
let btn = document.querySelector('button')
btn.addEventListener('click', test(() => console.log('2233'), 1000))
</script>
2、函数柯里化
(1)
(2)
(3)
<script>
function add() {
var arg = [...arguments]
let fn = function() {
arg.push(...arguments)
return fn
}
fn.toString = function() {
return arg.reduce((sum, cur) => sum + cur)
}
return fn
}
console.log(add(1)(2, 3).toString());
</script>
3、setTimeout实现setInterval
<script>
function myInterval(fn, delay) {
setTimeout(() => {
fn.call(this)
myInterval(fn, delay)
}, delay)
}
myInterval(() => {
console.log(123);
}, 1000)
</script>
三、字符串
1、清除字符串前后空格
(1) .replace()
(2) 循环
//(2)
function myTrim(str) {//记录前后空格的个数,最后对字符串进行截取
let first=0,last=str.length
for (let i in str) {
if (str[i]===' ') {
first++
} else {
break
}
}
for (let i=last;i>first;i--) {
if (str[i]===' ') {
last--
} else {
break
}
}
return str.substr(first,last-first)
}
2、驼峰命名
var s1 = "get-element-by-id"
// 转化为 getElementById
var f = function(s) {
return s.replace(/-\w/g, function(x) {
return x.slice(1).toUpperCase();
})
}
四、数组
1、 数组拍平
(1)、递归法
(2)、toString & split
<script>
function flatten(res) {
// split 会将 string 中按照括号里的内容进行分割并存入一个新数组,返回数组
// map 会对数组中的所有数据按照函数进行处理后返回一个处理后的数组
return res.toString().split(',').map((item) => {
return Number(item)
})
}
let res = [1, 2, 3, [4, 5, [6]]]
console.log(flatten(res));
</script>
(3)、join & split
function flatten(res) {
// join 把数组中的所有元素转换为一个字符串
// split 会将 string 中按照括号里的内容进行分割并存入一个新数组,返回数组
// map 会对数组中的所有数据按照函数进行处理后返回一个处理后的数组
return res.join().split(',').map((item) => {
return parseInt(item)
})
}
2、数组去重
<script>
let arr = [1, 2, 3, 3, 4, 4, 5, 6, 6]
//(1) set
/* arr = new Set(arr)
console.log(arr); */
//(2) indexOf
let res = []
for (let num of arr) {
if (res.indexOf(num) == -1) {
res.push(num)
}
}
console.log(res)
</script>
3、根据属性,对象数组去重
const responseList = [
{ id: 1, a: 1 },
{ id: 2, a: 2 },
{ id: 3, a: 3 },
{ id: 1, a: 4 },
];
const result = responseList.reduce((acc, cur) => {
const ids = acc.map(item => item.id);
return ids.includes(cur.id) ? acc : [...acc, cur];
}, []);
console.log(result); // -> [ { id: 1, a: 1}, {id: 2, a: 2}, {id: 3, a: 3} ]
4、数组乱序
// 著名的Fisher–Yates shuffle 洗牌算法
function shuffle(arr){
let m = arr.length;
while(m > 1){
let index = parseInt(Math.random() * m--);
[arr[index],arr[m]] = [arr[m],arr[index]]; //解构赋值
}
return arr;
}
五、浅拷贝、深拷贝
1、浅拷贝
// 1. ...实现
let copy1 = {...{x:1}}
// 2. Object.assign实现
let copy2 = Object.assign({}, {x:1})
2、深拷贝
<script>
// (1)
function deepClone(obj) {
if (obj == null || typeof obj != 'object') {
return obj
}
// let result = obj instanceof Array ? [] : {}
let result
if (obj instanceof Array) {
result = []
} else {
result = {}
}
for (let key in obj) { //in遍历键值,of遍历键名
if (obj.hasOwnProperty(key)) {
/* for in 可以遍历到myObject的原型方法method,如果不想遍历原型方法和属性的话,
所以for in更适合遍历对象,不要使用for in遍历数组
for of 遍历的只是数组内的元素,而不包括数组的原型属性method和索引name
for..of适用遍历数/数组对象/字符串/map/set等拥有迭代器对象的集合.但是不能遍历对象,因为没有迭代器对象
可以在循环内部判断一下,hasOwnPropery方法可以判断某属性是否是该对象的实例属性 */
result[key] = deepClone(obj[key])
}
}
return result
}
const obj = {
name: 'zhangsan',
address: {
city: 'beijing'
},
arr: ['a', 'b', 'c']
}
let obj1 = deepClone(obj)
obj1.address.city = '上海'
console.log(obj1)
console.log(obj);
//(2)
function deepClone2(obj) {
var _obj = JSON.stringify(obj),//将JavaScript值转换为JSON字符串
var objClone = JSON.parse(_obj);//将JSON字符串转为一个对象。
return objClone;
}
</script>
六、bind,apply,call
1、bind函数
2、apply函数
3、call函数
七、实现原生API
1、实现new
<script>
function myNew(constructor_fn, ...arg) {
let obj = new Object()
obj.__proto__ = constructor_fn.prototype
let result = constructor_fn.apply(obj, arg)
if (result instanceof Object) {
return result
} else {
return obj
}
}
function Person(uname, age) {
this.uname = uname;
this.age = age;
}
// let obj1 = new Person('vivian', 18);
let obj2 = myNew(Person, 'vivian', 18);
console.log(obj2);
2、实现create()
<script>
/* Object.create()方法用于创建一个新对象并将这个新对象的[[Prototype]]设置为特定的对象。另外,Object.create()方法还有一个可选的参数propertiesObject,
这个参数类似于Object.defineProperties()方法的第二个参数,指定了将要添加到新对象中的属性和属性的各个特性。 */
function myCreate(object, properties) {
// 创建构造函数,并将构造函数的原型对象设置为proto
function Fn() {}
Fn.prototype = object
//创建新对象
let obj = new Fn()
if (properties && typeof properties === 'object') {
Object.defineProperties(obj, properties)
}
return obj
}
//test
let person = myCreate({
name: "Alex"
}, {
age: {
writable: false,
enumerable: true,
value: 18
}
});
console.log(person.name, person.age);
</script>
3、instanceof实现
<script>
function myInstanceof(left, right) {
let left_proto = left.__proto__ //两个_
let right_proto = right.prototype
while (true) {
if (!left_proto) return false
if (left_proto == right_proto) return true
left_proto = left_proto.__proto__
}
}
console.log(myInstanceof([], Array));
</script>
4、实现reduce()
<script>
Array.prototype.myReduce = function(fn, init) {
let [val, index] = init ? [init, 0] : [this[0], 1] //this表示调用myReduce方法的数组
for (let i = index; i < this.length; i++) {
val = fn(val, this[i], i, this)
}
return val
}
let res = [1, 2, 3, 4, 5]
console.log(res.myReduce((val, next) => {
return val + next
}, 10)); //25
</script>
5、实现forEach
<script>
Array.prototype.myForEach = function(fn) {
for (let i = 0; i < this.length; i++) {
fn.call(this, this[i], i, this)
}
}
let res = [1, 2, 3, 4, 5]
res.myForEach((item, index, arr) => {
console.log(item);
})
</script>
6、用reduce实现map
<script>
//map 会返回一个新数组
Array.prototype.myMap = function(fn, value) {
if (typeof fn !== 'function') {
throw new TypeError("arguments[0] is not a function");
}
let arr = this
let res = []
arr.reduce((total, cur, index, arr) => {
res.push(fn.call(value, cur, index, arr)) // map 中将传入的第二个参数作为回调函数的 this 值
}, 0)
return res
}
let arr = [1, 2, 3, 4]
let res = arr.myMap((item) => {
return item * item
})
console.log(res);
</script>
7、实现indexOf
<script>
String.prototype.myIndexOf = function(s) {
//方法1:使用正则表达式
/* let reg = new RegExp(s)
let res = reg.exec(this) //exec() 方法检索字符串中的指定值。返回值是被找到的值。如果没有发现匹配,则返回 null
return res === null ? -1 : res.index */
//方法2:
let s_l = s.length
let str_l = this.length
let index = -1
if (s_l > str_l) return -1
for (let i = 0; i <= str_l - s_l; i++) {
if (this.substr(i, i + s_l) == s) {
index = i
break
}
}
return index
}
//test
let demo = 'ppap'
let str = 'a'
console.log(demo.myIndexOf(str))
</script>
8、实现Object.assign()
<script>
const obj1 = {
name: 'tou',
age: 10,
profile: {
a: 1
}
}
const obj2 = {
name: 'kinn',
age: 8,
profile: {
b: 2
},
email: 2233
}
function assign() {
if (arguments[1] == null) { // 注意点2
return arguments[2]
}
var to = Object(arguments[1]); // 注意点3
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) { // 注意点2
// 注意点4
for (var nextKey in nextSource) {
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
if (typeof nextSource[nextKey] == 'object') {
nextSource[nextKey] = assign({}, to[nextKey], nextSource[nextKey])
}
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
}
console.log(assign({}, obj1, obj2));
/*输出 {
name: 'kinn',
age: 8,
profile: {
a: 1,
b: 2
},
email: 2233
} */
</script>
<script>
const obj1 = {
name: 'tou',
age: 10,
profile: {
a: 1
}
}
const obj2 = {
name: 'kinn',
age: 8,
profile: {
b: 2
},
}
const obj3 = {
email: 2233,
name: 'mile'
}
function test(...arg) {
if (arg[0] == null) {
return arg[1]
}
let to = arg[0]
for (let i = 1; i < arg.length; i++) {
let next = arg[i]
if (next != null) {
for (let key in next) {
if (next.hasOwnProperty(key)) {
if (typeof next[key] == 'object') {
next[key] = test(to[key], next[key])
}
to[key] = next[key]
}
}
}
}
return to
}
console.log(test({}, obj1));
</script>
八、图片懒加载
<script>
let imgs = document.querySelectorAll('img')
function scrollimg() {
for (let img of imgs) {
if (img.offsetTop <= window.innerHeight + document.body.scrollTop) {
let data_src = img.getAttribute('data-src')
console.log(data_src);
img.setAttribute('src', data_src)
}
}
}
window.addEventListener('scroll', () => {
scrollimg()
})
scrollimg()
</script>
九、实现一个简单路由
1、hash路由
<body>
<div id="container">
<a href="#">首页</a>
<a href="#about">关于我们</a>
<a href="#user">用户列表</a>
</div>
<div id="context"></div>
<script>
class BaseRouter {
constructor() {
this.routes = {};
this.refresh = this.refresh.bind(this);
window.addEventListener('load', this.refresh);
window.addEventListener('hashchange', this.refresh);
};
// 对路由进行收集
route(path, cb) {
this.routes[path] = cb || function(){};
};
refresh() {
const path = `/${window.location.hash.slice(1) || ''}`;
this.routes[path]();
}
}
const Route = new BaseRouter();
Route.route('/about', () => changeText("关于我们页面"));
Route.route('/user', () => changeText("用户列表页"));
Route.route("/", () => changeText("首页页面"));
function changeText(arg) {
document.getElementById('context').innerHTML = arg;
}
</script>
</body>
2、history路由
<body>
<div class="router">
<a href="./">首页</a>
<a href="./user">用户列表</a>
<a href="./about">关于我们</a>
</div>
<div class="content"></div>
<script>
class BaseRouter {
constructor () {
// 路由表
this.routes = {}
}
// 收集路由
route (path, cb) {
this.routes[path] = cb || function () {}
}
// 访问路由
go (path) {
window.history.pushState({path}, null, path)
const cb = this.routes[path]
if (cb) cb()
}
}
//实例化路由对象
const Route = new BaseRouter()
//添加路由
Route.route('./', () => {changeText('这是首页页面')})
Route.route('./user', () => {changeText('这是用户页面')})
Route.route('./about', () => {changeText('这是关于我们页面')})
function changeText (text) {
let dom = document.querySelector('.content')
dom.innerHTML = text
}
document.querySelector('.router').addEventListener('click', e => {
if (e.target.tagName === 'A') {
e.preventDefault()
Route.go(e.target.getAttribute('href'))
}
})
</script>
</body>
十、 发布订阅模式(event bus)
<script>
class eventEmitter {
constructor() {
this.list = {} //创建事件缓存列表
}
//订阅消息
on(eventName, callBack) {
if (!this.list[eventName]) {
//当还没有这个事件时,为这个事件添加一个缓存列表
this.list[eventName] = [callBack]
} else {
//当已经存在这个事件的缓存列表之后,直接添加
this.list[eventName].push(callBack)
}
}
//发布消息
emit(eventName, ...args) {
this.list[eventName].forEach(fn => fn.apply(this, args))
}
//取消订阅
remove(eventName, callBack) {
this.list[eventName] = this.list[eventName].filter(fn => fn != callBack)
}
//只监听一次
once(eventName, callBack) {
const fn = () => {
callBack()
this.remove(eventName, fn)
}
this.on(eventName, fn)
}
}
</script>
十一、HTTP请求
1、jsonp
<script>
//1、创建回调函数
function fn(data) {
console.log(data);
}
//创建script元素
let newScript = document.createElement('script')
//绑定src
newScript.src = 'https://www.abc.com?callback=fn'
//将script元素插入文档
document.body.appendChild(newScript)
</script>
2、实现AJAX
(1)简易版
//get请求
let xhr = new XMLHttpRequest()
xhr.open('GET',url,true)
xhr.onreadystatechange = function(){
if(xhr.readystate === 4){
if(xhr.status >= 200 && xhr.status < 300){
console.log(xhr.resopnseText);
}
}
}
xhr.send(null)
//post请求
let xhr = new XLMHttpRequest()
xhr.open('POST',url,true)
xhr.onreadystatechange = function(){
if(xhr.readystate === 4){
if(xhr.status >= 200 && xhr.status < 300){
console.log(xhr.responseText);
}
}
}
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded;charset=UTF-8')
xhr.send(data)
(2)promise实现版
<script>
function ajax(url, method, data) {
let xhr = new XMLHttpRequest()
method = method.toUpperCase()
let p = new Promise((resolve, reject) => {
let param = ''
if (method == 'GET') {
if (data instanceof 'object') {
let arr = []
for (let key in data) {
arr.push(`${key}=${data[key]}`)
}
param = arr.join('&')
param = `?${param}`
}
xhr.open(method, url + param, true)
} else {
xhr.open(method, url, true)
}
xhr.onreadystatechange = function() {
if (xhr.readystate === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(new ERROR('error'))
}
}
}
if (method == 'GET') xhr.send(null)
else {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.send(param)
}
})
return p
}
</script>
十二、Promise系列
1、promise
<script>
const PEDDING = Symbol()
const FULFILLED = Symbol()
const REJECTED = Symbol()
const myPromise = function(fn) {
this.states = PEDDING
this.value = ''
const resolve = (value) => {
this.states = FULFILLED
this.value = value
}
const reject = (error) => {
this.states = REJECTED
this.value = error
}
try {
fn(resolve, reject)
} catch (error) {
reject(error)
}
this.then = (onFulFilled, onRejected) => {
if (this.states == FULFILLED) {
onFulFilled(this.value)
}
if (this.states == REJECTED) {
onRejected(this.value)
}
}
}
//test
let p = new myPromise((resolve, reject) => {
resolve(123)
})
p.then(value => {
console.log(value)
})
</script>
<script>
function myPromise(excutor) {
let self = this
self.status = 'pending'
self.value = null
self.reason = null
self.onFulfilledCallbacks = []
self.onRejectedCallbacks = []
function resolve(value) {
if (self.status === 'pending') {
self.value = value
self.status = 'fulfilled'
self.onFulfilledCallbacks.forEach(item => item())
}
}
function reject(reason) {
if (self.status === 'pending') {
self.reason = reason
self.status = 'rejected'
self.onRejectedCallbacks.forEach(item => item())
}
}
try {
excutor(resolve, reject)
} catch (err) {
reject(err)
}
}
myPromise.prototype.then = function(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(data) {
resolve(data)
}
onRejected = typeof onRejected === 'function' ? onRejected : function(err) {
throw err
}
let self = this
if (self.status === 'fulfilled') {
return new myPromise((resolve, reject) => {
try {
let x = onFulfilled(self.value)
if (x instanceof Promise) {
x.then(resolve, reject)
} else {
resolve(x)
}
} catch (err) {
reject(err)
}
})
}
if (self.status === 'rejected') {
return new myPromise((resolve, reject) => {
try {
let x = onRejected(self.reason)
if (x instanceof myPromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
} catch (err) {
reject(err)
}
})
}
if (self.status === 'pending') {
return new myPromise((resolve, reject) => {
self.onFulfilledCallbacks.push(() => {
let x = onFulfilled(self.value)
if (x instanceof myPromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
})
self.onRejectedCallbacks.push(() => {
let x = onRejected(self.reason)
if (x instanceof myPromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
})
})
}
}
myPromise.prototype.catch = function(fn) {
return this.then(null, fn)
}
let p = new myPromise((resolve, reject) => {
resolve(2233)
})
p.then((data) => {
p = new myPromise((res, rej)=>{
})
}).then((data) => {
console.log(data);
})
</script>
如上代码可以说明p1.then()的结果是一个与p1不同的promise对象。
换句话说,then()会封装一个全新的promise对象p2。那既然 p2也是一个promise对象,那么,p2的状态(promiseStatus)和值(promiseValue)分别是什么?
规则如下:
如果p1的状态是pending,则p2的状态也是pending。
•如果p1的状态是resolved,then()会去执行f_ok,则p2的状态由f_ok的返回值决定。
•如果f_ok返回值不是promise对象,则p2的状态是resolved,且p2的promiseValue就是f_ok函数的return值。•如果f_ok返回值是一个promise对象,则p2的状态及promiseValue以这个promise对象为准。•如果f_ok这个函数内部发生了错误(或者是用户主动抛出错误),则p2的状态是rejected,且p2的promiseValue就是这个错误对象。
如果p1的状态是rejected,then()会去执行f_err,则p2的状态由f_err的返回值决定。
•如果f_err返回值不是promise对象,则p2的状态是resolved,且p2的promiseValue就是f_err函数的return值。•如果f_err返回值是一个promise对象,则p2的状态及promiseValue以这个promise对象为准。
•如果f_err这个函数内部发生了错误(或者是用户主动抛出错误),则p2的状态是rejected,且p2的promiseValue就是这个错误对象。
2、promise.all
<script>
// !! 判断变量a为非空,未定义或者非空串才能执行方法体的内容
function isPromise(obj) {
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'
}
function myPromiseAll(arr) {
let res = []
return new Promise((reslove, reject) => {
for (let i = 0; i < arr.length; i++) {
if (isPromise(arr[i])) {
arr[i].then(data => {
res[i] = data
if (res.length == arr.length) {
reslove(res)
}
}).catch(error => {
reject(error)
})
} else {
res[i] = arr[i]
}
}
})
}
//test
var p1 = Promise.resolve('a');
var p2 = Promise.resolve('b');
var p3 = Promise.resolve('c');
/* Promise.all([p1, p2, p3]).then(function(value) {
console.log(value);
}) */
myPromiseAll([p1, p2, p3]).then(data => console.log(data))
</script>
3、promise.race
<script>
function myPromiseRace(arr) {
return new Promise((resolve, reject) => {
for (let i = 0; i < arr.length; i++) {
return arr[i].then(resolve, reject)
}
})
}
//test
const p1 = Promise.reject('a');
const p2 = Promise.resolve('b');
const p3 = Promise.resolve('c');
/* Promise.race([p1, p2, p3]).then(value => {
console.log(value);
}).catch(error => {
console.log(error);
}) */
myPromiseRace([p1, p2, p3]).then(value => {
console.log(value);
}).catch(error => {
console.log(error);
})
</script>
4、promise经典题
setTimeout(() => {
console.log(0); //9
}, 0);
new Promise((resolve, reject) => {
console.log(1); // 1
resolve()
}).then(() => {
console.log(2); //3
new Promise((resolve, reject) => {
console.log(3); //4
resolve()
}).then(() => {
console.log(4); //6
}).then(() => {
console.log(5); //8
})
}).then(() => {
console.log(6); //7
})
new Promise((resolve, reject) => {
console.log(7); //2
resolve()
}).then(() => {
console.log(8); //5
})
// 1 7 2 3 8 4 6 5 0
// then() 里面放入的是函数就会放进队列中等待执行,下一个then中的内容,得等上一个then中状态转变才会放入队列中
<script>
new Promise((res, rej) => {
console.log(1) //1
res()
}).then(() => {
console.log(2) //2
return new Promise((res, rej) => { //没有return 就变成123465
console.log(3) //3
res()
}).then(() => {
console.log(4) //4
}).then(() => {
console.log(5) //5
})
}).then(() => {
console.log(6) //6
})
//1 2 3 4 5 6
</script>
十三、链表
1、单链表
<script>
//节点类
function node(element) {
this.element = element
this.next = null
}
//链表类
class LinkedList {
constructor() {
this.head = null;
this.length = 0; // length 同数组 length 与下标关系
}
// 追加元素
append(element) {
let node = new Node(element);
let current = null; // 指针?
if (this.head === null) {
this.head = node;
} else {
current = this.head;
while (current.next) {
current = current.next;
}
current.next = node;
}
this.length++;
}
// 任意位置插入元素
insert(position, element) {
if (position >= 0 && position <= this.length) {
let node = new Node(element);
let current = this.head;
let previous = null;
let index = 0;
if (position === 0) {
node.next = current
this.head = node;
} else {
while (index++ < position) {
previous = current;
current = current.next;
}
node.next = current;
previous.next = node;
}
this.length++;
return true
}
return false
}
// 移除指定位置元素
removeAt(position) {
if (position > -1 && position < length) {
let current = this.head;
let previous = null;
let index = 0;
if (position === 0) {
this.head = current.next;
} else {
while (index++ < position) {
previous = current;
current = current.next;
}
previous.next = current.next;
}
this.length--;
return current.element;
}
return null
}
// 寻找元素下标
findIndex(element) {
let current = this.head;
let index = -1;
while (current) {
if (element === current.element) {
return index + 1;
}
index++;
current = current.next;
}
return -1;
}
isEmpty() {
return !this.length;
}
size() {
return this.length;
}
}
</script>
2、双向链表
<script>
class Node {
constructor(element) {
this.element = element;
this.prev = null;
this.next = null;
}
}
// 双向链表
class DoubleLinkedList {
constructor() {
this.head = null;
this.tail = null;
this.length = 0;
}
// 任意位置插入元素
insert(position, element) {
if (position >= 0 && position <= ehis.length) {
let node = new Node(element);
let current = this.head;
let previous = null;
this.index = 0;
// 首位
if (position === 0) {
if (!head) {
this.head = node;
this.tail = node;
} else {
node.next = current;
this.head = node;
current.prev = node;
}
} else if (position === this.length) { // 末尾
current = this.tail;
current.next = node;
node.prev = current;
this.tail = node;
} else { // 中间
while (index++ < position) {
previous = current;
current = current.next;
}
node.next = current;
previous.next = node;
current.prev = node;
node.prev = previous;
}
this.length++;
return true;
}
return false;
}
// 移除指定位置元素
removeAt(position) {
if (position > -1 && position < this.length) {
let current = this.head;
let previous = null;
let index = 0;
// 首位
if (position === 0) {
this.head = this.head.next
this.head.prev = null
if (this.length === 1) {
this.tail = null
}
} else if (position === this.length - 1) { // 末位
this.tail = this.tail.prev
this.tail.next = null
} else { // 中位
while (index++ < position) {
previous = current
current = current.next
}
previous.next = current.next
current.next.prev = previous
}
this.length--;
return current.element;
} else {
return null;
}
}
}
</script>
十四、数据双向绑定
(1)vue2.0
<input type="text" name="" id="">
<script>
let input = document.querySelector('input')
data = {}
Object.defineProperty(data, 'text', { //'text'表示监视的属性
// 数据变化 —> 修改视图
set(newVal) {
input.value = newVal
// console.log('input');
},
get() {
return input.value
}
})
// 视图更改 --> 数据变化
input.addEventListener('keyup', function(e) {
data.text = e.target.value
console.log(data.text);
})
</script>
(2)proxy双向数据绑定
<body>
<input type="text" id="input">
<span id="span"></span>
<script>
const input = document.getElementById('input')
const span = document.getElementById('span')
//设置代理对象
let obj = {}
let inputProxy = new Proxy(obj, {
//配置代理选项
get: function(target, key) {
return target[key]
},
set: function(target, key, value) {
if (key === 'text' && value) {
target[key] = value
span.innerHTML = value
}
}
})
//添加事件
input.addEventListener('keyup', function(e) {
inputProxy.text = e.target.value
console.log(inputProxy.text);
})
</script>
</body>
十五、判断两个对象是否相等
<script>
function isObjectEqual(obj1, obj2) {
let o1 = obj1 instanceof Object;
let o2 = obj2 instanceof Object;
if (!o1 || !o2) { // 如果不是对象 直接判断数据是否相等
return obj1 === obj2
}
// 判断对象的可枚举属性组成的数组长度
if (Object.keys(obj1).length !== Object.keys(obj2).length) {
return false;
}
for (let attr in obj1) {
let a1 = Object.prototype.toString.call(obj1[attr]) == '[object Object]'
let a2 = Object.prototype.toString.call(obj2[attr]) == '[object Object]'
let arr1 = Object.prototype.toString.call(obj1[attr]) == '[object Array]'
if (a1 && a2) {
// 如果是对象继续判断
if (!isObjectEqual(obj1[attr], obj2[attr])) {
return false
}
} else if (arr1) {
// 如果是对象 判断
if (obj1[attr].toString() != obj2[attr].toString()) {
return false;
}
} else if (obj1[attr] !== obj2[attr]) {
// 不是对象的就判断数值是否相等
return false
}
}
return true
}
let obj1 = {
name: 'amy',
eat: {
body: 1,
say: 'kk'
},
time: [1, 2, 3, 4, 5]
}
let obj2 = {
name: 'amy',
eat: {
body: 1,
say: 'kk'
},
time: [1, 2, 3, 4, 5],
}
console.log(isObjectEqual(obj1, obj2))
</script>
十六、判断一个对象是空对象
<script>
function test(obj) {
//(1) JSON.stringify
// if (JSON.stringify(obj) === '{}'){
// return true
// }
//(2)使用Object.keys()
if (Object.keys(obj).length == 0) {
return true
}
return false
}
console.log(test({
name: 'mile'
}));
</script>
十七、实现函数判断类型的函数
function getType(obj) {
if (obj === null) return String(obj);
return typeof obj === 'object'
? Object.prototype.toString.call(obj).replace('[object ', '').replace(']', '').toLowerCase()
: typeof obj;
}
// 调用
getType(null); // -> null
getType(undefined); // -> undefined
getType({}); // -> object
getType([]); // -> array
getType(123); // -> number
getType(true); // -> boolean
getType('123'); // -> string
getType(/123/); // -> regexp
getType(new Date()); // -> date
十八、数据绑定基本实现
题目:
// 实现一个方法,可以给 obj 所有的属性添加动态绑定事件,当属性值发生变化时会触发事件
let obj = {
key_1: 1,
key_2: 2
}
function func(key) {
console.log(key + ' 的值发生改变:' + this[key]);
}
bindData(obj, func);
obj.key_1 = 2; // 此时自动输出 "key_1 的值发生改变:2"
obj.key_2 = 1; // 此时自动输出 "key_2 的值发生改变:1"
答:
function bindData(obj, fn) {
for (let key in obj) {
Object.defineProperty(obj, key, {
set(newVal) {
if (this.value !== newVal) {
this.value = newVal;
fn.call(obj, key);
}
},
get() {
return this.value;
}
})
}
}
十九、有一个祖先树状 json 对象,当一个人有一个儿子的时候,其 child 为其儿子对象,如果有多个儿子,child 为儿子对象的数组。
请实现一个函数,找出这个家族中所有有多个儿子的人的名字(name),输出一个数组。
// 样例数据
let data = {
name: 'jack',
child: [
{ name: 'jack1' },
{
name: 'jack2',
child: [{
name: 'jack2-1',
child: { name: 'jack2-1-1' }
}, {
name: 'jack2-2'
}]
},
{
name: 'jack3',
child: { name: 'jack3-1' }
}
]
}
答:
function findMultiChildPerson(data) {
let nameList = [];
function tmp(data) {
if (data.hasOwnProperty('child')) {
if (Array.isArray(data.child)) {
nameList.push(data.name);
data.child.forEach(child => tmp(child));
} else {
tmp(data.child);
}
}
}
tmp(data);
return nameList;
}
二十、js继承
<script>
//1、原型链继承
//(1)所有子类共用一个父类实例,任何一个子类的来自父类的引用对象改变,会影响所有子类
//(2)无法向父类构造函数传参
function Father() {
this.name = 'mile'
}
Father.prototype.getName = function() {
console.log(123);
}
function Son() {
this.age = 19
}
Son.prototype = new Father()
Son.prototype.constructor = Son
let son = new Son()
console.log(son.name);
console.log(son.age);
son.getName()
//2、构造函数继承
//(1)能向父类传参,多个子类不共享一个原型实例
//(2)不能使用父类原型上的方法
function Father() {
this.name = 'mile'
}
Father.prototype.getName = function() {
console.log(123);
}
function Son() {
this.age = 19
Father.call(this, arguments)
}
Son.prototype.constructor = Son
let son = new Son('kinn')
console.log(son.name);
console.log(son.age);
son.getName() //会报错
//3、组合式继承
//(1)能向父类传参,多个子类不共享一个原型实例
//(2)能使用父类原型上的方法
//(3)但是每次创建实例时会调用两次父类构造函数(new Father(), Father.call())
function Father() {
this.name = 'mile'
}
Father.prototype.getName = function() {
console.log(123);
}
function Son() {
this.age = 19
Father.call(this, ...arguments)
}
Son.prototype = new Father()
Son.prototype.constructor = Son
let son = new Son('kinn')
console.log(son.name);
console.log(son.age);
son.getName()
//4、寄生式继承
//将指向父类实例改为指向父类原型,从而减少一次构造函数的执行
function Father() {
this.name = 'mile'
}
Father.prototype.getName = function() {
console.log(123);
}
function Son() {
Father.call(this, ...arguments)
this.age = 18
}
Son.prototype = Object.create(Father.prototype)
Son.prototype.constructor = Son
let son = new Son('kinn')
console.log(son.name);
console.log(son.age);
son.getName()
</script>