原理实现
call
()
- 不传入第一个参数,那么上下文默认为
window
- 改变了
this
指向,让新的对象可以执行该函数,并能接受参数
Function.prototype.myCall = function(context) {
if (typeof this !== 'function') {
throw Error("not a function")
}
context = context || window
context.fn = this
const args = [...arguments].slice(1)
const result = context.fn(...args)
delete context.fn
return result
}
```
- 首先 `context` 为可选参数,如果不传的话默认上下文为 `window`
- 接下来给 `context` 创建一个 `fn` 属性,并将值设置为需要调用的函数
- 因为 `call` 可以传入多个参数作为调用函数的参数,所以需要将参数剥离出来
- 然后调用函数并将对象上的函数删除
```
let foo = { value: 1 }
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call2(foo, 'black', '18') // black 18 1
apply
()
apply
的实现也类似,区别在于对参数的处理
Function.prototype.myApply = function(context) {
if (typeof this !== 'function') {
throw Error("not a function")
}
context = context || window
context.fn = this
let result
// 处理参数和 call 有区别
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
bind()
bind
需要返回一个函数,需要判断一些边界问题
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
const _this = this
const args = [...arguments].slice(1)
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以 new F(),所以需要判断
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}
分析区别:
bind
返回了一个函数,对于函数来说有两种方式调用,一种是直接调用,一种是通过new
的方式,我们先来说直接调用的方式- 对于直接调用来说,这里选择了
apply
的方式实现,但是对于参数需要注意以下情况:因为bind
可以实现类似这样的代码f.bind(obj, 1)(2)
,所以我们需要将两边的参数拼接起来,于是就有了这样的实现args.concat(...arguments)
- 最后来说通过
new
的方式,在之前的章节中我们学习过如何判断this
,对于new
的情况来说,不会被任何方式改变this
,所以对于这种情况我们需要忽略传入的this
防抖
即短时间内大量触发同一事件,只会执行一次函数,防抖常用于搜索框/滚动条的监听事件处理,如果不做防抖,每输入一个字/滚动屏幕,都会触发事件处理,造成性能浪费;实现原理为设置一个定时器,约定在xx毫秒后再触发事件处理,每次触发事件都会重新设置计时器,直到xx毫秒内无第二次操作。
// func是用户传入需要防抖的函数
// wait是等待时间
const debounce = (func, wait = 50) => {
// 缓存一个定时器id
let timer = 0
// 这里返回的函数是每次用户实际调用的防抖函数
// 如果已经设定过定时器了就清空上一次的定时器
// 开始一个新的定时器,延迟执行用户传入的方法
return function(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, args)
}, wait)
}
}
节流
防抖是延迟执行
,而节流是间隔执行
,和防抖的区别在于,防抖每次触发事件都重置定时器,而节流在定时器到时间后再清空定时器,函数节流即每隔一段时间就执行一次
,实现原理为设置一个定时器,约定xx毫秒后执行事件,如果时间到了,那么执行函数并重置定时器
。
// func是用户传入需要防抖的函数
// wait是等待时间
const throttle = (func, wait = 50) => {
// 上一次执行该函数的时间
let lastTime = 0
return function(...args) {
// 当前时间
let now = +new Date()
// 将当前时间和上一次执行函数时间对比
// 如果差值大于设置的等待时间就执行函数
if (now - lastTime > wait) {
lastTime = now
func.apply(this, args)
}
}
}
setInterval(
throttle(() => {
console.log(1)
}, 500),
1
)
深复制
- 会不会忽略
undefined
- 会不会忽略
symbol
- 能不能序列化函数
- 能不能解决循环引用的对象
- 在遇到函数、
undefined
或者symbol
的时候,该对象能不能正常的序列化 - 处理原型链
- 处理DOM
- 处理Date
- 处理Reg
- 处理ES6类
- 处理null
- 处理boolen
- 处理array
- 处理string
- 处理number
- 处理自定义对象
function cloneObject(target, source) {
//获取该对象的所有属性的集合
var propNames = Object.getOwnPropertyNames(source)
for (let i = 0 i < names.length i++) {
//获取属性的描述对象
var desc = Object.getOwnPropertyDescriptor(source, propNames[i])
//解决多层对象与循环引用的问题,同时处理嵌套的DOM、正则、实例、Date等特殊对象
if (typeof desc.value === "object" && desc.value !== null) {
var obj
//单独处理DOM;用原型链判断DOM类型
if (desc.value instanceof HTMLElement) {
//使用document.createElement(标签名)创建DOM
obj = document.createElement(desc.value.nodeName)
} else {
//其他对象使用constructor判断类型---大写;使用 new constructor(arguments)构建
switch (desc.value.constructor) {
case Box:
obj = new desc.value.constructor(desc.value[Box.ARG[0]], desc.value[Box.ARG[1]])
break
case RegExp://(正则,修饰符)
obj = new desc.value.constructor(desc.value.source,desc.value.flags)
break
default :
obj = new desc.value.constructor()
}
}
//递归多层对象
cloneObject(obj, desc.value)
//然后在设置属性的描述对象
Object.defineProperty(target, propNames[i], {
value:obj,
enumerable:desc.enumerable,
writable:desc.writable,
configurable:desc.configurable
})
}
else {
//第一层不是对象或者为null直接使用Object.defineProperty自定义属性
Object.defineProperty(target, propNames[i], desc)
}
}
return target
}
var obj1 = cloneObject({}, obj)
obj.d.k.m = 100
console.log(obj1)
var obj = {
a:1,
b:"a",
c:false,
d:{
e:undefined,
f:null,
g:[1, 2, 3, 4, 5],
h:new Date(),
i:/^[a-z]{2,4}$/gi,
j:new Box(4, 5),
k:{}
}
}
class Box {
static ARG = ["a", "b"]
constructor(a1, b1) {
this.a = a1
this.b = b1
}
play() {
console.log(this.a1 + this.b1)
}
}
Object.defineProperties(obj.d.k, {
l:{ value:10 },
m:{
configurable:true,
writable:true,
value:20
},
n:{
enumerable:true,
value:function() {
console.log("aaaa")
}
},
o:{
value:new Image()
}
})
轮播图
export default class Utils{
static ce(type, style, data) {
var elem = document.createElement(type);
Object.assign(elem.style, style);
return elem;
}
}
import Utils from "./Utils.js";
export default class Carousel {
widthRem = 0;
heightRem = 0;
bnArray = [ ];
position = 0;
direction = "left";
imgConX = 0;
speed=0.5;
auto=true;
_dotBool=true;
static carouselList=[ ];
constructor( _imgList = [ ], _bnList = [ ], _width = screen.width,
_height = screen.width / 3) {
this.fontSize = parseInt(getComputedStyle(document.documentElement).fontSize);
this.widthRem = _width / this.fontSize;
this.heightRem = _height / this.fontSize;
this.imgList = _imgList;
this.bnList = _bnList;
this.elem = this.createElem( );
Carousel.carouselList.push(this);
}
createElem( ) {
if (this.elem) return this.elem;
var carousel = Utils.ce("div", {
width: this.widthRem + "rem",
height: this.heightRem + "rem",
position: "relative",
overflow: "hidden",
});
this.createImgCon(carousel);
this.createBn(carousel);
this.createDot(carousel);
carousel.addEventListener("mouseenter", e => { this.mouseHandler(e) });
carousel.addEventListener("mouseleave", e => { this.mouseHandler(e) });
//初次执行变色函数,给第一个小圆点设置样式
this.changePre( );
window.addEventListener("resize", e => { this.resizeHandler(e) });
this.resizeHandler( );
return carousel;
}
appendTo(parent) {
parent.appendChild(this.elem);
//设置圆点位置
this.dot.style.left = (this.widthRem - this.dot.offsetWidth / 100) / 2 + "rem";
}
source(list){
this.imgList = list;
}
createImgCon(parent) {
this.imgCon = Utils.ce("div", {
width: this.widthRem * 2 + "rem",
height: this.heightRem + "rem",
position: "absolute",
left: 0
});
var img = Utils.ce("img", {
width: this.widthRem + "rem",
height: this.heightRem + "rem",
});
img.src = this.imgList[0];
this.imgCon.appendChild(img);
parent.appendChild(this.imgCon);
}
createBn(parent) {
for (let i = 0; i < this.bnList.length; i++) {
var bn = Utils.ce("img", {
position: "absolute",
top: (this.heightRem - 0.6) / 2 + "rem",
left: i === 0 ? "0.3rem" : "none",
right: i === 1 ? "0.3rem" : "none"
});
bn.src = this.bnList[i];
parent.appendChild(bn);
this.bnArray.push(bn);
bn.addEventListener("click", e => { this.clickHandler(e) });
}
}
set dotBool(value){
this._dotBool=value;
this.dot.style.display=value ? "block" :"none"
}
get dotBool( ){
return this._dotBool;
}
createDot(parent) {
this.dot = Utils.ce("ul", {
position: "absolute",
listStyle: "none",
padding: "0px",
margin: "0px",
bottom: "0.4rem",
// display:this.dotBool ? "block" :"none"
});
for (let i = 0; i < this.imgList.length; i++) {
let dots = Utils.ce("li", {
width: "0.2rem",
height: "0.2rem",
borderRadius: "0.15rem",
float: "left",
marginLeft: i === 0 ? "0rem" : "0.1rem",
border: "2px solid #FF0000"
});
this.dot.appendChild(dots);
}
parent.appendChild(this.dot);
this.dot.addEventListener("click", e => { this.dotClickHandler(e) });
}
changePre( ) {
if (this.pre) {//this
this.pre.style.backgroundColor = "rgba(255,0,0,0)";
}
this.pre = this.dot.children[this.position];
this.pre.style.backgroundColor = "rgba(255,0,0,0.8)";
}
createNextImg( ) {//this
var img = Utils.ce("img", {
width: this.widthRem + "rem",
height: this.heightRem + "rem",
})
img.src = this.imgList[this.position];
if (this.direction === "left") {
this.imgCon.appendChild(img);
this.imgConX = 0;
} else if (this.direction === "right") {
this.imgCon.insertBefore(img, this.imgCon.firstChild);
this.imgConX = -this.widthRem;
}
this.imgCon.style.left = this.imgConX + "rem";
this.bool = true;
this.changePre ( );
}
mouseHandler(e) {//this
if(!this.auto) return;
if (e.type === "mouseenter") {
this.autoBool = false;
} else if (e.type === "mouseleave") {
this.autoBool = true;
this.time = 200;
}
}
clickHandler(e) {//this
if (this.bool) return;
if (e.currentTarget.src.indexOf("left") < 0) {
this.direction = "left";
this.position++;
if (this.position > this.imgList.length - 1) this.position = 0;
} else {
this.direction = "right";
this.position--;
if (this.position < 0) this.position = this.imgList.length - 1;
}
this.createNextImg( );
}
dotClickHandler(e) {//this
if (this.bool) return;
if (e.target.nodeName !== "LI") return;
var dotList = Array.from(this.dot.children);
var index = dotList.indexOf(e.target);
this.direction = index > this.position ? "left" : "right";
this.position = index;
this.createNextImg();
}
resizeHandler(e) {//this
document.documentElement.style.fontSize = innerWidth / screen.width * this.fontSize + "px";
}
update( ) {
this.imgConMove( );
this.autoMove( );
}
imgConMove( ) {//this
if(!this.bool)return;
if(this.direction==="left"){
this.imgConX-=this.speed;
this.imgCon.style.left=this.imgConX+"rem";
if(this.imgConX<-this.widthRem){
this.bool=false;
this.imgConX=0;
this.imgCon.firstElementChild.remove();
this.imgCon.style.left=this.imgConX+"rem";
}
}else if(this.direction==="right"){
this.imgConX+=this.speed;
this.imgCon.style.left=this.imgConX+"rem";
if(this.imgConX>=0){
this.bool=false;
this.imgConX=0;
this.imgCon.lastElementChild.remove();
this.imgCon.style.left=this.imgConX+"rem";
}
}
}
autoMove( ) {//this
if (!this.autoBool) return;
this.time--;
if (this.time > 0) return;
this.time = 200;
var evt = new MouseEvent("click");
this.bnArray[1].dispatchEvent(evt);
}
dispose( ){
let index=Carousel.carouselList.indexOf(this);
if(index<0) return;
Carousel.carouselList.splice(index,1);
}
static UPDATE( ){
for(let i=0;i<Carousel.carouselList.length;i++){
Carousel.carouselList[i].update( );
}
}
}
<style>
/* 全局设置要单独设置 ,rem*/
html{
font-size: 100px;
}
body{
font-size: 16px;
margin: 0;
}
</style>
</head>
<body>
<script type="module">
import Carousel from "./js/Carousel.js";
var bnArr=["./img/left.png","./img/right.png"];
var arr=["./img/a.jpg","./img/b.jpg","./img/c.jpg","./img/d.jpg","./img/e.jpg"]
let carousel=new Carousel(arr,bnArr,600,200)
carousel.appendTo(document.body)
</script>
发布—订阅模式(观察者模式)
// 发布订阅模式
class EventEmitter {
constructor() {
// 事件对象,存放订阅的名字和事件
this.events = {};
}
// 订阅事件的方法
on(eventName,callback) {
if (!this.events[eventName]) {
// 注意时数据,一个名字可以订阅多个事件函数
this.events[eventName] = [callback]
} else {
// 存在则push到指定数组的尾部保存
this.events[eventName].push(callback)
}
}
// 触发事件的方法
emit(eventName) {
// 遍历执行所有订阅的事件
this.events[eventName] && this.events[eventName].forEach(cb => cb());
}
// 移除订阅事件
removeListener(eventName, callback) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter(cb => cb != callback)
}
}
// 只执行一次订阅的事件,然后移除
once(eventName,callback) {
// 绑定的时fn, 执行的时候会触发fn函数
let fn = () => {
callback(); // fn函数中调用原有的callback
this.removeListener(eventName,fn); // 删除fn, 再次执行的时候之后执行一次
}
this.on(eventName,fn)
}
}
Promise的实现
class Promise {
constructor(executorCallBack) {
//value变量用于保存resolve 或者reject 中传入的值
//fulfillAry和rejectedAry用于保存then中的回调,因为当执行完Promise时状态可能还是等待中,这时候应该把then中的回调保存起来用于状态改变时使用
this.status = 'pending';
this.value = undefined;
this.fulfillAry = [];
this.rejectedAry = [];
//=>执行Excutor
//首先两个函数都得判断当前状态是否为等待中,因为规范规定只有等待态才可以改变状态
//将当前状态更改为对应状态,并且将传入的值赋值给value;遍历回调数组并执行
let resolveFn = result => {
if (this.status !== 'pending') return;
//为了保证函数执行顺序,需要将两个函数体代码使用setTimeout包裹起来
let timer = setTimeout(() => {
this.status = 'fulfilled';
this.value = result;
this.fulfillAry.forEach(item => item(this.value));
clearTimeout(timer);
}, 0);
};
let rejectFn = reason => {
if (this.status !== 'pending') return;
let timer = setTimeout(() => {
this.status = 'rejected';
this.value = reason;
this.rejectedAry.forEach(item => item(this.value));
clearTimeout(timer);
}, 0);
};
//执行传入的参数并且将之前两个函数当做参数传进去
//可能执行函数过程中会遇到错误,需要捕获错误并且执行reject函数
try {
executorCallBack(resolveFn, rejectFn);
} catch (err) {
//=>有异常信息按照rejected状态处理
rejectFn(err);
}
}
//then第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数
//then返回的是一个新的promise, 将以回调的返回值来resolve.
//执行fulfilledCallBack或rejectedCallBack函数时会返回一个x
//判断x是否为对象或者函数,如果都不是的话,将x传入resolve中;如果x是对象或者函数的话,先把x.then赋值给then,然后判断then的类型,如果不是函数类型的话,就将x传入resolve中。
then(fulfilledCallBack, rejectedCallBack) {
fulfilledCallBack= typeof fulfilledCallBack === 'function' ?
fulfilledCallBack : result => result;
rejectedCallBack= typeof rejectedCallBack === 'function' ?
rejectedCallBack:reason => throw new Error(reason)
//每次then的函数返回值都会被Promise.resolve重新包装
return new Promise((resolve, reject) => {
this.fulfillAry.push(() => {
try {
let x = fulfilledCallBack(this.value);
x instanceof Promise ? x.then(resolve, reject) : resolve(x);
} catch (err) {
reject(err)
}
});
this.rejectedAry.push(() => {
try {
let x = this.rejectedCallBack(this.value);
x instanceof Promise ? x.then(resolve, reject) : resolve(x);
} catch (err) {
reject(err)
}
})
});
}
//返回一个promise对象
static resolve(value) {
//value值是一个Promise对象,则直接返回该对象
if (value instanceof Promise) return value
return new Promise(resolve => resolve(value))
}
//返回一个状态为失败的Promise对象
static reject(value) {
//并将给定的失败信息传递给对应的处理方法
return new Promise((resolve, reject) => reject(value))
}
}
//如果在resolve后再throw错误,是不会被catch到的,因为状态改变后不可逆)
catch(rejectedCallBack) {
return this.then(null, rejectedCallBack);
}
static all(promiseAry = []) {
let index = 0,
result = [];
return new Promise((resolve, reject) => {
for (let i = 0; i < promiseAry.length; i++) {
promiseAry[i].then(val => {
index++;
result[i] = val;
if (index === promiseAry.length) {
resolve(result)
}
}, reject);
}
})
}
static race(promiseAry) {
return new Promise((resolve, reject) => {
if (promiseAry.length === 0) {
return;
}
for (let i = 0; i < promiseAry.length; i++) {
promiseAry[i].then(val => {
resolve(val);
return;
}, reject);
}
})
}
vue手写Tabbar
<template>
<div>
<footer>
<ul>
<router-link tag="li" to="/home" class="flex">
<i class="image-icons icon-home"></i>
<span>首页</span>
</router-link>
<router-link tag="li" to="/category" class="flex">
<i class="image-icons icon-category"></i>
<span>分类</span>
</router-link>
<router-link tag="li" to="/discover" class="flex">
<i class="image-icons icon-star"></i>
<span>星球</span>
</router-link>
<router-link tag="li" to="/cart" class="flex">
<i class="image-icons icon-cart"></i>
<span>购物车</span>
</router-link>
<router-link tag="li" to="/user" class="flex">
<i class="image-icons icon-user"></i>
<span>我的</span>
</router-link>
</ul>
</footer>
</div>
</template>
<style lang='scss' scoped>
ul {
height: 100%;
display: flex;
align-items: center;
.flex {
flex: 1 1 auto
flex-direction: column;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
span {
margin-top: 3px;
}
}
}
.router-link-active {
color: #ff6700;
.image-icons {
background-color: #ff6700;
}
}
.icon-home{
background-image: url(../assets/images/icon-home.c1947eda40.png)
}
.icon-category{
background-image: url(../assets/images/icon-category.be93273636.png)
}
.icon-star{
background-image: url(../assets/images/icon-star.d051789804.png)
}
.icon-cart{
background-image: url(../assets/images/icon-cart.585c3aa4d3.png)
}
.icon-user{
background-image: url(../assets/images/icon-user.23496a0116.png)
}
</style>
react手写tabbar
import React, { Component } from 'react'
import {
BrowserRouter as Router,
Route,Link,useLocation, Redirect,Switch} from 'react-router-dom'
import HomePage from './Home'
import Position from './Position'
import Login from './Login'
import User from './User'
export default class index extends Component {
constructor() {
super();
this.state = {
islogin: false
}
this.routers = [
{
path: "/home", component: HomePage, auth: false
},
{
path: "/position", component: Position, auth: true
}, {
path: "/user", component: User, auth: true
}
]
}
handleLogin = () => {
this.setState({
islogin: true
})
}
render() {
return (
<Router>
<Link to="/home">首页</Link>
<Link to="/position">职位管理</Link>
<Link to="/user">用户管理</Link>
{
this.routers.map((item) => {
return
<Route path={item.path}
render={ ( ) => {
return item.auth?
(this.state.islogin ? <item.component></item.component> :
<Login login={this.handleLogin}></Login>)
:<item.component></item.component>
}}>
</Route>
})
}
</Router>
)
}
}
//tapbar2
<Link onClick={this.changeFontColor.bind(this,3)} className={`${3 === this.state.active ? 'active-menu-li-lan active-menu-li' : 'active-menu-li'}`} to="/index/teachers">名师堂</Link>
柯里化
利用闭包原理在执行可以形成一个不销毁的作用域,然后把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个最少参数函数。
前端使用柯里化的用途主要就是简化代码结构,提高系统的维护性,一个方法,只有一个参数,强制了功能的单一性,很自然就做到了功能内聚,降低耦合。
function curry(fn, args) {
var length = fn.length;
var args = args || [];
return function () {
var newArgs = args.concat(Array.prototype.slice.call(arguments));
if (newArgs.length < length) {
return curry.call(this, fn, newArgs);
} else {
return fn.apply(this, newArgs);
}
}
}
function multiFn(a, b, c) {
return a * b * c;
}
var multi = curry(multiFn);
multi(2)(3)(4);
multi(2, 3, 4);
multi(2)(3, 4);
multi(2, 3)(4);
快速排序
//快速排序时间复杂度O(nlog(2)n)-O(log(n2)),速度最快:希尔排序 O(n)-O(n2);
//原理:取数组中间位置的数据,跟所有数据比较,比该数小的放一左数组,大的放右数组,递归,最后拼接数据,得到想要的数组
//重新赋值给元素替换
Array.prototype.quickSort = function ( ) {
var arr=this
if (arr.length <= 1) { return arr; }
var item = arr.splice(parseInt(arr.length / 2), 1)[0];
// var item = arr.splice(Math.floor(arr.length / 2), 1)[0];
var leftArr = [];
var rightArr = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] < item) {
leftArr.push(arr[i]);
} else {
rightArr.push(arr[i]);
}
}
return leftArr.quickSort().concat([item],rightArr.quickSort());
};
var arr = [1, 2, 34, 2, 1, 3, 2, 8]
arr=arr.quickSort();
console.log(arr)
斐波那契数列
function fib(n) {
let array = new Array(n + 1).fill(null)
array[0] = 0
array[1] = 1
for (let i = 2; i <= n; i++) {
array[i] = array[i - 1] + array[i - 2]
}
return array[n]
}
fib(10)
拖拽
Object.defineProperties(HTMLElement.prototype, {
drag: {
enumerable: true,
set: function (value) {
this._drag = Boolean(value);
if (this._drag) {
this.style.position = "absolute";
this.addEventListener("mousedown", this.mouseHandler)
} else {
this.removeEventListener("mousedown", this.mouseHandler)
}
},
get: function () {
return this._drag;
}
},
mouseHandler: {
value: function (e) {
if (e.type === "mousedown") {
this.offsetPoint = { x: e.offsetX, y: e.offsetY };
document.elem = this;
document.addEventListener("mousemove", this.mouseHandler);
document.addEventListener("mouseup", this.mouseHandler);
} else if (e.type === "mousemove") {
Object.assign(document.elem.style, {
left: e.clientX - document.elem.offsetPoint.x + "px",
top: e.clientY - document.elem.offsetPoint.y + "px",
})
} else if (e.type === "mouseup") {
document.removeEventListener("mousemove", document.elem.mouseHandler)
document.removeEventListener("mouseup", document.elem.mouseHandler)
}
}
}
});
var div = document.querySelector('div')
div.drag = true
instanceOf
function instanceOf(left, right) {
let proto = left.__proto__;
let prototype = right.prototype
while (true) {
if (proto === null) return false
if (proto === prototype) return true
proto = proto.__proto__;
}
}
寄生组合式继承
Function.prototype.extend = function (supClass) {
function F() { }
// 将父类的原型赋值给这个中间替代类
F.prototype = supClass.prototype
// 将原子类的原型保存
var proto = this.prototype
// 将子类的原型设置为中间替代类的实例对象
this.prototype = new F()
// 将原子类的原型复制到子类原型上,合并超类原型和子类原型的属性方法
var names = Object.getOwnPropertyNames(proto);
for (var i = 0; i < names.length; i++) {
var desc = Object.getOwnPropertyDescriptor(proto, names[i]);
Object.defineProperty(subClass.prototype, names[i], desc);
}
// 设置子类的构造函数时自身的构造函数,以防止因为设置原型而覆盖构造函数
this.prototype.constructor = this
// 给子类的原型中添加一个属性,可以快捷的调用到父类的原型方法
this.prototype.superClass = supClass.prototype
// 如果父类的原型构造函数指向的不是父类构造函数,重新指向
if (supClass.prototype.constructor !== supClass) {
supClass.prototype.constructor = supClass
}
}
new
操作符
function New(func) {
var res = {};
if (func.prototype !== null) {
res.__proto__ = func.prototype;
}
var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
return ret;
}
return res;
}
var obj = New(A, 1, 2);
// equals to
var obj = new A(1, 2);
判断是否是移动端
var browser={
versions:function() {
var u = navigator.userAgent
var app = navigator.appVersion;
return {//移动终端浏览器版本信息
trident: u.indexOf('Trident') > -1, //IE内核
presto: u.indexOf('Presto') > -1, //opera内核
webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核
mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或者uc浏览器
iPhone: u.indexOf('iPhone') > -1 , //是否为iPhone或者QQHD浏览器
iPad: u.indexOf('iPad') > -1, //是否iPad
webApp: u.indexOf('Safari') == -1 //是否web应用程序,没有头部与底部
};
}(),
language:(navigator.browserLanguage || navigator.language).toLowerCase()
}
if(browser.versions.mobile || browser.versions.ios || browser.versions.android ||
browser.versions.iPhone || browser.versions.iPad) {
window.location = "https://m.mobile.com";
}