1.call和apply的区别是什么,哪一个性能更好一些?
call和apply都是Function原型的方法,每一个函数作为Function的实例都可以调用这两个方法。都可以改变this指向。call和apply唯一的区别就是call传参数是一个一个传,apply是传一个数组,在数组里传入参数。
call性能要略好一些,尤其是参数超过三个的时候
2.实现(5).add(3).minus(2)
Number.prototype.add = function(n){
n = Number(n); //提高容错率
n = isNaN(n)? 0:n;
return this + n; //返回的也得是个Number对象 这样才能链式调用
}
Number.prototype.minus = function(n){
n = Number(n);
n = isNaN(n)? 0:n;
return this - n; //返回的也得是个Number对象 这样才能链式调用
}
console.log((5).minus(4).add(1));
3.箭头函数与普通函数有什么区别?构造函数可以使用new生成实例,那么箭头函数可以吗?为什么?
箭头函数与普通函数区别
1.箭头函数更加简洁
2.箭头函数没有自己的this,它里面出现的this从属于函数所处的上下文 (使用call、apply等都无法改变this指向)
3.箭头函数没有arguments
4.箭头函数不能被new执行(因为箭头函数没有this也没有prototype)
4.实现一个字符串匹配算法,从字符串S中查找是否存在字符串T,若存在,返回所在位置,否则返回-1.(不能基于indexOf,includes等内置方法)
String.prototype.myIndexOf = function (T){
let lenT = T.length;
let lenS = this.length;
let res = -1;
if (lenT > lens) return -1;
for(let i= 0;i<lenS-lenT+1;i++){
if(this.substr(i,lenT) === T){
res = i;
break;
}
}
return res;
}
let S = 'haohaoxuexi'
let T = 'xue';
console.log(S.myIndexOf(T));
5.输出下面代码运行结果
//example1
var a = {}, b = '123', c=123;
a[b] = 'b';
a[c] = 'c';
console.log(a[b]); // 'c' 对象中数字属性名和字符串属性名没区别 a['123'] <=> a[123]
//example 2
var a = {}, b = Symbol('123'), c=Symbol('123;);
a[b] = 'b';
a[c] = 'c';
console.log(a[b]); // 'b' Symbol创建出来的值是唯一值 所以Symbol('123') !== Symbol('123')//example3
var a={}, b={key:'123'}, c={key:'456'};
a[b]='b';
a[c]='c';
console.log(a[b]); // 'c' 对象是不能作为另一个对象的属性名的,所以对象作为key会被转化为字符串。而对象
//的toString方法会把对象转化成'[Object Object]' 所以a里面是 {'[object Object]': "c"}
// obj = {} arr=[12,23] obj[arr]='老王' obj:{"12,23":"老王"} Array的toString和Object的toString还有点区别
6.变态原型链
function Foo(){
Foo.a = function () {
console.log(1);
}
this.a = function() {
console.log(2);
}
}
//把Foo当做类,在原型上设置实例共有的方法 实例.a();
Foo.prototype.a = function() {
console.log(3);
}
//把Foo当做普通对象设置私有的属性方法 Foo.a();
Foo.a = function () {
console.log(4);
}
Foo.a(); //4
let obj = new Foo(); // 把Foo函数执行一遍,于是Foo.a被覆盖成输出1, 并且原型方法a也被覆盖成输出2
obj.a(); // 2
Foo.a(); //1
7.数组扁平化 编写一个程序 将数组扁平化,并去除其中重复部分数据,最终得到一个升序且不重复的数组
1.使用ES6中的Array.prototype.flat处理
arr = arr.flat(Infinity)
arr = Array.from(new Set(arr)).sort((a,b) =>a-b);
2.把数组直接变成字符串即可
arr = arr.toString().split(',').map(item => Number(item));
arr = Array.from(new Set(arr)).sort((a,b) =>a-b);
8.手写new
function Dog(name){
this.name = name;
}
Dog.prototype.bark = function(){
console.log('wangwang');
}
Dog.prototype.sayName = function(){
console.log('my name is ' + this.name);
}
要求:基于内置的new关键词,我们可以创建dog的一个实例sanmao,实例可调用原型方法和属性
现在的需求是:自己实现一个_new方法 也能模拟内置的new后的结果
new之后要做的事情:
1.创建一个类的实例:创建一个空对象obj,将obj.__proto__设置为构造函数的prototype
2.初始化实例:构造函数被传入参数并调用,this指针被设定为指向该实例obj
3.返回实例obj
function _new(Fn,...arg){
let obj = {};
obj.__proto__ = Fn.prototype;
Fn.call(obj,...arg);
return obj;
}
let sanmao = _new(Dog,'三毛');
9.变量作用域
let fn = function AAA() {
AAA = 1000;
console.log(AAA); //非严格模式下打印这个函数的内容 严格模式报错
};
AAA(); //Uncaught ReferenceError: AAA is not defined
//1.本该匿名的函数如果设置了函数名,在外面还是无法调用,但是在函数里面是可以使用的
//2.而且类似于创建常量一样,这个名字存储的值不能在修改。在非严格模式下不报错,但是不会有任何效果,严格模式直接报错
//我们可以把AAA理解成是用const创建的
所以:
var b = 10;
(function b() {
b = 20;
console.log(b); //输出函数本身
})();
console.log(b); //10
如果想让里面的b输出20 外面的b输出10,则里面的b就要是私有的(var、let、const声明或者在函数里加个形参b)
10.如何让 a==1 && a==2 && a==3同时成立?
==在进行比较的时候,如果两边数据类型不一样,则先转换为相同的数据类型
1.{} == {} 两个对象比较,比较的是堆内存的地址
2. null == undefined true null === undefined false
3.NaN == NaN 不相等 NaN和谁都不相等
4.[12] == '12' 对象和字符串比较 是把对象toString()转换为字符串后再比较
5. 剩余的情况在比较时候 都是转换为数字(前提是数据类型不一样)
对象转数字:先转字符串再转数字
字符串转数字:只要出现一个非数字字符,结果就是NaN
布尔转数字:true--1 false -- 0
null 转数字 0
undefined 转数字 NaN
[12]==true false
[] == false true
[] == 1 false
true == 2 false
var a = ?
if(a == 1 && a ==2 && a == 3) {
console.log(1);
}
因为对象和数字比较 先把对象.toString(),而toString这个方法是定义在原型上的,我们可以定义在对象本身截胡这个toString,进而实现我们的目的
//方法一
var a = {
n:0,
toString: function(){
return ++this.n;
}
};
//方法二
let a = [1,2,3];
a.toString = a.shift; //删除数组第一项 并且把删除的内容返回
11.对象调用push方法
let obj = {
2:3,
3:4,
length: 2,
push: Array.prototype.push
}
obj.push(1);
obj.push(2);
console.log(obj);
//////////////////
{
2: 1
3: 2
length: 4
push: ƒ push()}
先看Array.prototype.push:(源码模拟)
Array.prototype.push = function (val) {
this[this.length] = val;
//this.length 自动+1 !!很重要
return this.length
}
所以对象调用push方法的时候,
obj.push(1); // this:obj => obj[obj.length] = 1 => obj[2]=1 => obj.length = 3
obj.push(2); //this:obj => obj[obj.length] = 2 => obj[3]=2 =>obj.length = 4
所以输出的时候键值对被替换 length变成了4
12.冒泡排序
function bubble(ary) {
let temp=null;
for(var i=0;i<ary.length-1;i++){
for(let j=0;j<ary.length-1-i;j++){
if(ary[j] > ary[j+1]){
temp = ary[j];
ary[j] = ary[j+1];
ary[j+1] = temp;
}
}
}
return ary;
}
13.插入排序
function insert(arr){
let handle = [];
handle.push(arr[0]);
for(var i=1;i<arr.length;i++){
for(var j=handle.length-1;j>=0;j--){ //从后往前比 都可以
if(arr[i] > handle[j]) {
handle.splice(j+1,0,arr[i]);
break;
}
if(j===0){ //上面那种情况之后 在这里判定的只会是arr[i]比handle开头的都要小
handle.unshift(arr[i]);
}
}
}
}
14. 快速排序
function quick(arr){
if(arr.length<=1){
return arr;
}
let midIndex=Math.floor(arr.length/2);
let midValue = arr.splice(midIndex,1)[0];
let arrLeft=[],arrRight=[];
for(let i=0;i<arr.length;i++){
let item=arr[i];
item<midValue?arrLeft.push(item):arrRight.push(item);
}
return quick(arrLeft).concat(midValue,quick(arrRight));
}
15.对象转数组
let obj = {
1:222,
2:123,
5:888,
} //这里的key代表月份
请把数据处理为如下结构
[222,123,null,null,888,null,null,null,null,null,null,null]
数组一共表示十二个月 其中1 2 5月在对象中有值 请把对象转换成这个数组
//方法一
let arr = new Array(12).fill(null).map((item,index) => {
return obj[index+1] || null;
});
//方法二
obj.length = 13; //加上length属性就可以调用Array.from方法了
let arr = Array.from(obj).splice(1).map(item => {
return typeof item === "undefined" ? null : item;
})
//方法三
let arr = new Array(12).fill(null);
Object.keys(obj).forEach(item => {
arr[item - 1] = obj[item];
});
16.旋转数组
function rotate(k) {
if (k<0 || k === 0|| k === this.length) return this;
if(k > this.length) k = k%this.length;
return this.slice(-k).concat(this.slice(0,this.length-k));
}
Array.prototype.rotate = rotate;
let arr = [1,2,3,4,5,6,7];
arr.rotate(3);
17.手写map
关于为什么可以用Object.prototype.toString.call()判断 数据类型:
参考JavaScript:Object.prototype.toString方法的原理 - 知春里 - 博客园 (cnblogs.com)
Array.prototype.myMap = function(fn,context){
if (Object.prototype.toString.call(fn) !== "[object Function]"){
throw ('Error');
}
let resArray = [];
let curArray = this;
for( let i =0;i<curArray.length;i++){
resArray[i] = fn.call(context, curArray[i], i,curArray);
}
return reaArray;
}
let arr = [3,4,5,6,7];
arr.myMap((item,index,arr) => {return item + 1})