JavaScript Codes
数列
1 在Javascript中什么是伪(类)数组?如何将伪数组转化为标准数组?
伪数组具有以下特点的对象:
- 按索引方式存储数据;
- 具有length属性;
- 没有数组的push、shift、pop等方法;
如function的arguments对象,还有getElementsByTagName、ele.childNodes等返回的NodeList对象,或者自定义的某些对象,这些都可以是伪数组。如:
var guiseArr={
'0':"AAA",
'1':"BBB",
'2':"CCC",
length:3
}
我们可以通过以下几种方式将伪数组转换为标准数组:
//第一种方法,使用slice
Array.prototype.slice.call(arr,0);
//第二种方法,直接使用for循环
var newArr=[];
for(var i=0;i<arr.length;i++){
newArr=arr[i];
}
//第三种方法,使用ES6的Array.from() 方法用于通过拥有 length 属性的对象或可迭代的对象来返回一个数组。
var newArr=Array.from(arr);
//f附加:Array.from(object, mapFunction, thisValue)
var arr = Array.from([1, 2, 3], x => x * 10);
// arr[0] == 10;
// arr[1] == 20;
// arr[2] == 30;
获取字符串中频率最高的字符
let str = "ahbbccdeddcccddfg";
String.prototype.getMostOften = function () {
let str = this;
let obj = {};
let max = 0;
for (let i = 0; i < str.length; i++) {//记录各字符串及对应次数
if (obj[str[i]]) {
obj[str[i]]++
} else {
obj[str[i]] = 1
}
};
for (let key in obj) {//记录最大次数
max = max < obj[key] ? obj[key] : max
}
for (let key in obj) {//输出最大次数及对应字符
if (obj[key] === max) {
console.log(`最多的字符是${key},一共出现了${max}次`);
}
}
}
str.getMostOften();//最多的字符是c,一共出现了5次 最多的字符是d,一共出现了5次
斐波那契数列
function fib(num) {
if (num === 0) return 0;
if (num === 1) return 1;
return fib(num - 2) + fib(num - 1);//arguments.callee(num-2)+arguments.callee(num-1);
}
fib(6) // 8
取数组的最大值(ES5、ES6)?
答:
//ES5 的写法
Math.max.apply(null, [14, 3, 77, 30]);
//ES6 的写法
Math.max(...[14, 3, 77, 30]);
//reduce
[14, 3, 77, 30].reduce((accumulator, currentValue) => {
return accumulator = accumulator > currentValue ? accumulator : currentValue
});
附注:reduce()
https://www.jianshu.com/p/541b84c9df90
多维数组降维:
var array = [1, [2], [3, [4, [5]]]];
array.toString().split(',')//["1", "2", "3", "4", "5"] 注意光是split不够得 会将数字转为字符串 需要处理
array.toString().split(',').map(e=>+e)//[1, 2, 3, 4, 5]
//reduce()
function flat(arr){
return arr.reduce(
(pre,item) => pre.concat(Array.isArray(item) ? flat(item) : item),
[]
)
}
//redue()
const flattenDeep = (arr) => Array.isArray(arr)
? arr.reduce( (a, b) => [...a, ...flattenDeep(b)] , [])
: [arr]
用js实现随机选取10–100之间的10个数字,存入一个数组,并排序
var arr = [];
function getRandom(start, end) {
return Math.floor(Math.random() * (end - start + 1) + start)
};
var i = 10;
while (i) {
arr.push(getRandom(10, 100))
i--;
};
arr.sort()
ES5
new
作用:
- 创建一个空对象,继承构造函数原型,并且this引用该对象,
- 属性与方法被加入到this引用的对象中
- 新建的对象由this引用,最后隐式返回this
步骤:
使用new命令时,它后面的函数依次执行下面的步骤。
- 创建一个空对象,作为将要返回的对象实例。
- 将这个空对象的原型,指向构造函数的prototype属性。
- 将这个空对象赋值给函数内部的this关键字。
- 开始执行构造函数内部的代码。
注意事项:
如果构造函数内部有return语句,且return后面跟着一个对象,new命令会返回return语句指定的对象;否则,就会不管return语句,返回this对象。
new命令内部流程
function _new(constructor, params) {
let args = [].slice.call(arguments, 1);
let obj = Object.create(constructor.prototype);
let ctx = constructor.apply(obj, args);
return (typeof ctx === 'object' && ctx != null) ? ctx : obj
}
代码详解:
function _new(/* 构造函数 */ constructor, /* 构造函数参数 */ params) {
var args = [].slice.call(arguments);// 将 arguments 对象转为数组
console.log(args); // [ƒ, "张三", 28]
var constructor = args.shift();// 取出构造函数
console.log(constructor); //ƒ () {this.price = 1000;return { price: 2000 };}
console.log(constructor.prototype);//{color: "red", constructor: ƒ}
var context = Object.create(constructor.prototype); // 创建一个空对象,继承构造函数的 prototype 属性 实现context.__proto__ == constructor.prototype
console.log(context);//Father {} 见下图
console.log(context.price);//undefined
var result = constructor.apply(context, args); // 执行构造函数 进行属性添加 并捕捉有可能返回的返回值
console.log(context);//Father {price: 1000} 见下图
console.log(context.price);//1000
console.log(result);//{price: 2000}
return (typeof result === 'object' && result != null) ? result : context; // 如果返回结果是对象,就直接返回,否则返回 context 对象
}
var Father = function () {
this.price = 1000;
return { price: 2000 };
};
Father.prototype.color = 'red'
// 实例
var chid = _new(Father, '张三', 28);
console.log(chid)//{price: 2000}
JS如何实现继承
步骤:
第一步是在子类的构造函数中,调用父类的构造函数。
第二步,是让子类的原型指向父类的原型,这样子类就可以继承父类原型。
ES5
function Afn(Aname){
this.name = Aname;
this.gender = '男'
};
Afn.prototype.sayName = function(){console.log(this.name +''+this.gender)};
function Bfn(Bname,age){
Afn.apply(this,arguments);
this.age = age//自定义
};
Bfn.prototype = Object.create(Afn.prototype) //或者 Bfn.prototype = new Afn()
Bfn.prototype.sayAge = ()=>{console.log(this.age)}//自定义 非法
let bfn = new Bfn('henry',12)
bfn.sayName()//henry男
bfn.sayAge()//undefined this指向了window(箭头函数this为定义时的所在环境对象)
console.log(bfn instanceof Bfn);//true
console.log(bfn instanceof Afn);//true
console.log(Bfn instanceof Afn);//fals
ES6
class Afn{
constructor(name){
this.name = name;
this.gender = '女';
this.body = '漂亮'
};
sayName(){console.log(this.name +''+this.gender)};
getBody(){return this.body}
}
class Bfn extends Afn{
constructor(name,age){
super(name);
this.age = age;
};
getBody(){console.log(this.age + '岁,真'+super.getBody() +'!');
}
}
let bfn = new Bfn('小黄',17)
bfn.sayName()//小黄女
bfn.getBody()//17岁,真漂亮!
console.log(bfn instanceof Bfn);//true
console.log(bfn instanceof Afn);//true
console.log(Bfn instanceof Afn);//false
单个方法继承
上面代码中,子类是整体继承父类。有时只需要单个方法的继承,这时可以采用下面的写法。
ClassB.prototype.print = function() {
ClassA.prototype.print.call(this);
// some code
}
上面代码中,子类B的print方法先调用父类A的print方法,再部署自己的代码。这就等于继承了父类A的print方法。
多重继承
JavaScript 不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能。
function M1() {
this.hello = 'hello';
}
function M2() {
this.world = 'world';
}
function S() {
M1.call(this);
M2.call(this);
}
// 继承 M1
S.prototype = Object.create(M1.prototype);
// 继承链上加入 M2
Object.assign(S.prototype, M2.prototype);
// 指定构造函数
S.prototype.constructor = S;
var s = new S();
s.hello // 'hello'
s.world // 'world'
上面代码中,子类S同时继承了父类M1和M2。这种模式又称为 Mixin(混入)。
实现Object.create()
附:Object.create()详解
原理:让newObj的原型对象是oldObj (以前是这个函数的prototype 那么就让一个构造函数的prototype = oldObj 再new 这个函数即可)
if (typeof Object.create !== 'function') {
Object.create = function (obj) {
function F() {}
F.prototype = obj;
return new F();
};
}
上面代码表明,Object.create方法的实质是新建一个空的构造函数F,然后让F.prototype属性指向参数对象obj,最后返回一个F的实例,从而实现让该实例继承obj的属性。
JSONP:
https://juejin.im/post/5d2547d36fb9a07ea33c3cfd
深拷贝和浅拷贝
-
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存,浅拷贝只复制对象的第一层属性;
-
深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。对对象的属性进行递归复制。
let a = {
age: 1
}
let b = a
a.age = 2
console.log(b.age) // 2
从上述例子中我们可以发现,如果给一个变量赋值一个对象,那么两者的值会是同一个引用,其中一方改变,另一方也会相应改变。
通常在开发中我们不希望出现这样的问题,我们可以使用浅拷贝来解决这个问题。
浅拷贝
对象:
Object.assign
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1
展开运算符(…)
let a = {
age: 1
}
let b = {...a}
a.age = 2
console.log(b.age) // 1
赋值实现
function simpleClone(initalObj) {
var obj = {};
for(var i in initalObj) {
obj[i] = initalObj[i];
}
return obj;
}
数组
slice
var arr = [1,2,3,4];
var arr2 = arr.slice()
concat
var arr = [1,2,3,4];
var arr2 = [].concat(arr)
展开运算符(…)
通常浅拷贝就能解决大部分问题了,但是当我们遇到如下情况就需要使用到深拷贝了
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = {...a}
a.jobs.first = 'native'
console.log(b.jobs.first) // native
浅拷贝只解决了第一层的问题,如果接下去的值中还有对象的话,那么就又回到刚开始的话题了,两者享有相同的引用。要解决这个问题,我们需要引入深拷贝。
深拷贝
添加到Object原型上避免考虑是否是基本数据类型
Object.prototype.clone = function(){
var o = this.constructor === Array ? [] : {};
for(var e in this){
o[e] = typeof this[e] === "object" ? this[e].clone() : this[e];
}
return o;
}
JSON.parse(JSON.stringify(object))来解决。
let b = JSON.parse(JSON.stringify(a))
但是该方法也是有局限性的:
- 会忽略 undefined
- 会忽略 symbol
- 会忽略函数
- 会忽略正则与date对象
- 不能序列化函数
let a = {
age: undefined,
sex: Symbol('male'),
jobs: function() {},
name: 'yck'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // {name: "yck"}
你会发现在上述情况中,该方法会忽略掉函数和 undefined 。
五种常见数据类型递归遍历
(可以对 JavaScript 中的五种主要数据类型(Number、string、Object、Array、Boolean)进行复制):
精简版(不考虑性能)
function deepClone(source) {
if (typeof obj !== 'object' && obj == null) return;
let target = Array.isArray(source) ? [] : {};
//let target = obj.constructor === Array ? [] : {}
for (let key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (typeof source === 'object' && source != null) {
target[key] = deepClone(source[key])
} else {
target[key] = source[key]
}
}
};
return target;
};
考虑数组性能用while来做
function clone(Obj) {
if (null == obj || "object" != typeof obj) return obj;//考虑到null 基本数据类型直接赋值
var buf;
if (Obj instanceof Array) {
buf = []; //创建一个空的数组
var i = Obj.length;
while (i--) {
buf[i] = clone(Obj[i]);
}
return buf;
}else (Obj instanceof Object){
buf = {}; //创建一个空对象
for (var k in Obj) { //为这个对象添加新的属性
buf[k] = clone(Obj[k]);
}
return buf;
}
}
考虑到data对象且考虑数组性能用for来做
function clone(obj) {
var copy;
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
定义一个log方法,让它可以代理console.log的方法
log(){
console.log.apply(console,arguments)
}
如何实现一个 call 函数
Function.prototype.myCall = function (context) {
var context = context || window
// 给 context 添加一个属性
// getValue.call(a, 'yck', '24') => a.fn = getValue
context.fn = this
// 将 context 后面的参数取出来
var args = [...arguments].slice(1)
// getValue.call(a, 'yck', '24') => a.fn('yck', '24')
var result = context.fn(...args)
// 删除 fn
delete context.fn
return result
}
如何实现一个bind函数
Function.prototype.myApply = function (context) {
var context = context || window
context.fn = this
var result
// 需要判断是否存储第二个参数
// 如果存在,就将第二个参数展开
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
如何实现一个bind函数
ES6
实现call apply bind
bind
简单实现:
Function.prototype.bind = function(ctx){
var _this = this;
_this.call(ctx,[].slice.call(arguments,1))
};
功能封装
post & get 流程
var xhr = new XMLHttpRequest();
//get
xhr.open('GET', url + '?' + params, true);
xhr.send(null);
//post
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', '...');
xhr.send(params)//username=huanger&password=123
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {//判断异步调用是否完成
if (status >= 200 && xhr.status < 300) {判断异步调用是否成功
success(xhr.responseText || xhr.responseXML);//成功回调函数
} else {
fail(xhr.status)失败回调函数
}
}
}
Ajax简单封装
function Ajax(opts) {
let url = opts.url;
let type = (params.type || 'GET').toUpperCase();
let dataType = opts.dataType || "json";
let success = opts.success;
let onerror = opts.onerror;
let data = opts.data || {};
let dataStr = [];
for (let key in data) {
dataStr.push(key + '=' + data[key])
};
dataStr = dataStr.join('&');
if (type === 'GET') {
url += '?' + dataStr
};
let xhr = new XMLHttpRequest();
xhr.open(type, url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {//判断异步调用是否完成
if (status >= 200 && xhr.status < 300) {判断异步调用是否成功
success(xhr.responseText || xhr.responseXML);//成功回调函数
} else {
fail(xhr.status)失败回调函数
}
}
}
if (type == 'GET') {
xhr.send(null);
} else {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
xhr.send(dataStr);
}
}
原生JS实现Jq的ready方法?
function ready(fn){
if(document.addEventListener) { //标准浏览器
document.addEventListener('DOMContentLoaded', function() {
//注销事件, 避免反复触发
document.removeEventListener('DOMContentLoaded',arguments.callee, false);
fn(); //执行函数
}, false);
}else if(document.attachEvent) { //IE
document.attachEvent('onreadystatechange', function() {
if(document.readyState == 'complete') {
document.detachEvent('onreadystatechange', arguments.callee);
fn(); //函数执行
}
});
}
};
封装一个函数,参数是定时器的时间,.then执行回调函数
function sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
写一段JS程序提取URL中的各个GET参数
有这样一个URL:http://item.taobao.com/item.htm?a=1&b=2&c=&d=xxx&e,请写一段JS程序提取URL中的各个GET参数
(参数名和参数个数不确定),将其按key-value形式返回到一个json结构中,如{a:'1', b:'2', c:'', d:'xxx', e:undefined}
function serilizeUrl(url) {
var result = {};
url = url.split("?")[1];
var map = url.split("&");
for(var i = 0, len = map.length; i < len; i++) {
result[map[i].split("=")[0]] = map[i].split("=")[1];
}
return result;
}
清除字符串前后的空格
function trim(str) {
if (str && typeof str === "string") {
return str.replace(/(^\s*)|(\s*)$/g,""); //去除前后空白符
}
}
实现一个函数,判断输入是不是回文字符串
function run(input) {
if (typeof input !== 'string') return false;
return input.split('').reverse().join('') === input;
}
时间戳转为日期格式
//时间戳转换为自定义年月日时分秒时间格式:
//number :传入时间戳 时间戳为10位需*1000,时间戳为13位的话不需乘1000
//format :返回格式,支持自定义 但参数必须与formatArr里保持一致
function formatTime(number, format) {
//格式化日期函数,如月、日、时、分、秒保证为2位数
function formatNumber(n) {
n = n.toString()
return n[1] ? n : '0' + n
}
let date = new Date(number) //转为时间对象 不带参数的话返回当前日期
let newArr = []
let formatArr = ['Y', 'M', 'D', 'h', 'm', 's']
newArr.push(formatNumber(date.getFullYear()))
newArr.push(formatNumber(date.getMonth() + 1))
newArr.push(formatNumber(date.getDate()))
newArr.push(formatNumber(date.getHours()))
newArr.push(formatNumber(date.getMinutes()))
newArr.push(formatNumber(date.getSeconds()))
for (let i = 0; i < newArr.length; i++) {
format = format.replace(formatArr[i], newArr[i]) //字符串一一对应替换
}
return format
}
var timestamp = 1488481383;//时间戳
console.log(formatTime(timestamp, 'Y/M/D h:m:s'));//转换为日期:2017/03/03 03:03:03
console.log(formatTime(timestamp * 1000, 'h:m'));//转换为日期:03:03
DOM
写一个函数,批量操作 css。
function css(node, styleObj) {
//补全
}
css(document.body, {
"color": "red",
"background-color": "#ccc"
})
答:
function css(node, styleObj) {
for(key in styleObj) {
node.style[key] = styleObj[key]
}
}
css(document.body, {
"color": "red",
"background-color": "#ccc"
})
补全代码,要求:当鼠标放置在 li 元素上,会在 img-preview 里展示当前 li 元素的 data-img 对应的图片。
<ul class="ct">
<li data-img="1.png">鼠标放置查看图片1</li>
<li data-img="2.png">鼠标放置查看图片2</li>
<li data-img="3.png">鼠标放置查看图片3</li>
</ul>
<div class="img-preview"></div>
<script>
//补全
</script>
答:
<script>
var list = document.querySelector(".ct");
var preview = document.querySelector(".img-preview");
var newimg = document.createElement("img");
list.addEventListener("mouseover", function(e) {
if(e.target.tagName.toLowerCase() === "li") {
newimg.src = e.target.getAttribute("data-img");
preview.appendChild(newimg);
}
});
list.addEventListener("mouseout", function() {
preview.removeChild(newimg);
});
</script>
取到页面中所有的checkbox
var domList = document.getElementsByTagName(‘input’)
var checkBoxList = [];
var len = domList.length; //缓存到局部变量
while (len--) { //使用while的效率会比for循环更高
if (domList[len].type == ‘checkbox’) {
checkBoxList.push(domList[len]);
}
}
想实现一个对页面某个节点的拖曳?如何做?(使用原生JS)
- 给需要拖拽的节点绑定mousedown, mousemove, mouseup事件
- mousedown事件触发后,开始拖拽
- mousemove时,需要通过event.clientX和clientY获取拖拽位置,并实时更新位置
- mouseup时,拖拽结束
- 需要注意浏览器边界的情况
下面这个ul,如何点击每一列的时候alert其index
考察闭包
<ul id=”test”>
<li>这是第一条</li>
<li>这是第二条</li>
<li>这是第三条</li>
</ul>
var lis=document.getElementById('2223').getElementsByTagName('li');
for(var i=0;i<3;i++)
{
lis[i].onclick=(function(a){
return function() {
alert(a);
}
})(i);
}