前端一些小的知识点ZF

1、call和apply哪个性能更好?
call 的性能要比 apply 稍微好点,尤其是传给函数的参数超过三个以上的时候。

2、console.time
console.time 可以测试出一段代码执行的时间:

console.time('A');
// code goes here ...
console.timeEnd('A');	// 输出代码执行需要的时间

3、箭头函数和普通函数的区别?

  • 箭头函数语法上比普通函数简洁;
  • 箭头函数没有 this, 它里面的 this 是继承函数所处的上下文的this,使用call/apply也无法改变;
  • 箭头函数中没有 arguments (类数组),只能基于 ...arg 获取传递的参数集合(数组);
  • 箭头函数不能被 new 执行,因为箭头函数没有 this 和 prototype;

4、回调函数

  • 把一个函数B作为实参传递给另一个函数A,函数A执行的时候,可以把传进来的函数B去执行;
  • 回调函数里的this一般都是window;
  • 执行n次,可传值,可修改this指向;
function each(callback) {
  var a = this
  for(var i=0; i<this.length; i++) {
  	// 修改this指向,可传值
    var flag = callback.call(a, a[i], i);
    if(flag === false) {
      break;
    }
    console.log(a[i])
  }
}
Array.prototype.each = each;

var arr = [10,20,30,40];
arr.each(function(item, index){
  if(index > 2) {
    return false;
  }
});

5、对象属性名的问题

// example 1
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是ES6新增的数据类型;
// typeof Symbol('123') === 'symbol'
// 创建出来的是唯一值,Symbol('123') 不等于 Symbol('123')

// example 3
var a = {}, b = {key: '123'}, c = {key: '456'};
a[b] = 'b';
a[c] = 'c';
console.log(a[b]);	// 'c'
// 1. 对象的属性名不能是一个对象(遇到对象属性名,会转换为字符串)。
// obj={};  arr=[12,34];  obj[arr]='xx';  obj=>{"12,34": "xx"}
// 普通对象.toString()调取的是Object.prototype上的方法(这个方法是用来检测数据类型的)
// obj={};  obj.toString() => "[object Object]"
// obj[b] = 'b' => obj["[object Object]"] = 'b'

6、验证是否为一个网址
[思路]:用正则。

// URL格式
// 1.协议://	http/https/ftp
// 2.域名: www.baidu.com | baidu.com | pan.baidu.com
// 3.请求路径: / | /index.html | /stu/ | /stu/index.html
// 4.问号传参:?xxx=xxx&xxx=xxx
// 5.哈希值:#xxx
var url = "http://www.baidu.com/index.html?name=xxx&age=20#nav";
var reg = /^((http|https|ftp):\/\/)?(([\w-]+\.)+[a-z0-9]+)((\/[^/?#]*)+)?(\?[^#]+)?(#.+)?$/i;
// ^在[]里面代表非
// .:匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 \. 。

7、类和普通对象的属性方法

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();	// obj可以调取原型上的方法	Foo.a: f=>1		obj.a:f=>2
obj.a();	// 2	私有属性中有a
Foo.a();	// 1

8、正则中的正向预查

// 验证6-16位的字符串,必须同时有字母数字下划线
var reg = /(?!^[a-z0-9]+$)(?!^[A-Z0-9]+$)(?!^[a-zA-Z]+$)^[a-zA-Z0-9]{6,16}$/;

9、数组扁平化
方法一:Array.prototype.flat()

const arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

方法二:先转成字符串,再把每项转成数字

// 数组toString之后,不管有多少级,最终都变成以逗号分隔的字符串,没有中括号和所谓的层级
arr = arr.toString().split(',').forEach(item => {
	return Number(item);
});

方法三:JSON.stringify()

var str = JSON.stringify(arr);	// "[1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]]"
str.replace(/(\[|\])/g, '');	// "1, 2, 3, 4, 5, 6, 7, 8, 9, 10"

方法四:arr.some + 展开运算符

// some检测当数组的每一项还有没有是数组的
while(arr.some(item => Array.isArray(item))) {
	arr = [].concat(...arr)
}

方法五:自己实现递归

function flatArr() {
  let result = [];
  let fn = ary => {
    for (let i=0; i<ary.length; i++) {
      let item = ary[i];
      if (Array.isArray(item)) {
        fn(item);
        continue;
      }
      result.push(item);
    }
  }
  fn(this);
  return result;
}

Array.prototype.flatArr = flatArr;

const arr = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
console.log(arr.flatArr());

最后:去重且排序:

// ES6中的 new Set() 去重,返回一个对象
// [...new Set(arr)]
// Array.from(newSet(arr))
arr = Array.from(newSet(arr)).sort((a,b) => a-b);

10、自己实现new
考察是否知道 new 的过程:

  1. 向普通函数执行一样,形成一个私有的作用域(形参复制,变量提升);
  2. 默认创建一个对象,让函数中的this指向这个对象,这个对象就是当前类的一个实例;
  3. 代码执行;
  4. 默认把创建的对象返回。
function Rifle(name) {
  this.name = name;
}
Rifle.prototype.fire = function() {
  console.log(this.name + ' dadadadada');
}
Rifle.prototype.reload = function() {
  console.log(this.name +' reload the gun');
}

// 内置new关键字
var m416 = new Rifle('m416');
m416.fire();

// 自己实现new
function _new(Fn, ...arg) {
  // 创建一个空对象,让他的原型链指向Fn.prototype(作为Fn的一个实例)
  let obj = {};
  obj.__proto__ = Fn.prototype;
  // 或者 使用Object.creat([AA]) 简化上面两行
  // 创建一个空对象obj,并且让obj作为AA对象所属构造函数的实例(obj.__proto__==AA)
  let obj = Object.create(Fn.prototype);
  Fn.call(obj, ...arg);
  return obj;
}
var akm = _new(Rifle, 'akm');
akm.reload();

11、本应匿名的函数有了函数名

  1. 本应匿名的函数如果设置了函数名,在外面无法调用,但是再函数里面可以使用;
  2. AA类似于创建常量,这个名字存储值不能再被修改。(非严格模式下不报错,但是没有任何效果,严格模式下报错,可以理解为const创建出来的)。
let fn = function AA() {
	console.log(AA);	// 当前函数
	"use strict"
	AA = 100;	// "TypeError: Assignment to constant variable.
}
AA()	// "ReferenceError: AA is not defined
fn();

一个题目:

var b = 10;
(function b() {
	b = 20;
	console.log(b);	// 函数,因为不能被修改
	// 如果想要输出20,可把b = 20改为var b = 20(b要是私有的);
})()
console.log(b);	// 10

12、两个数对比
== 进行比较的时候,如果左右两边数据类型不一样,则先转换为相同的数据类型,然后进行比较:

  1. {} == {}:两个对象相比,比较的是堆内存的地址;
  2. null == undefined:相等;
  3. NaN == NaN:不相等,NaN和谁都不相等;
  4. [12] == '12:对象和字符串相比,把对象toString()转换为字符串后再对比;
  5. 剩余所有情况,都是转换为数字(前提数据类型不一样):
    • 对象转数字:先转成字符串,再转成数字;
    • 字符串转数字:只要出现一个非数字字符,就是NaN;
    • 布尔转数字:true->1, false->0;
    • null:0;
    • undefined:NaN。
// 例如:
[12] == true	// false Number([12].toString())
[] == false	// true
[] == 1	// false
"1" == 1	//true
true == 2	//false

题目:
有没有可能 (a == 1 && a ==2 && a == 3) 为 true

// 第一种:对象实现
// 对象和数字对比,先对象.toString()变成字符串,再转数字。
var a = {
	n: 0,
	// 私有的属性方法
	toString: function () {
		return ++this.n;
	}
}
// 此时调的不是Object.prototype.toString,而是私有的toString
if (a == 1 && a ==2 && a == 3) {
	console.log('success');
}

// 第二种:数组实现:
// shift()删除数组第一项,把删除的内容返回,原数组改变
let a = [1, 2, 3];
a.toString = a.shift;

// 第三种:Object.defineProperty()
Object.defineProperty(window, 'a', {
	get: function() { 
		this.value ? this.value++ : this.value = 1;
		return this.value;
	}
})

13、一个关于数组push的对象题

let obj = {
	2: 3,
	3: 4,
	length: 2,
	push: Array.prototype.push
}
obj.push(1);	// obj[2] = 1
obj.push(2);	// obj[3] = 2
console.log(obj)	// obj = {2: 1, 3:2, length: 4, push: xxx}

思路主要是明白push方法的实现:

Array.prototype.push = function (val) {
	this[this.length] = val;
	// this.length加1
	return this.length;
}

14、三大经典排序算法
①冒泡排序:
冒泡排序的思想:让数组中的当前项和后一项进行比较,如果当前项比后一项大,则两项交换位置。
冒泡排序
需要比较 length-1 轮,5个数只需要把4个最大的依次放到末尾。

function bubble(arr) {
  let temp = null;
  // 外层循环控制比较的轮数
  for (let i=0; i<arr.length-1; i++) {
    // 内层循环控制每一轮比较的次数
    for (let j=0; j<arr.length-1-i; j++) {
      if (arr[j] > arr[j+1]) {
        temp = arr[j];
        arr[j] = arr[j+1];
        arr[j+1] = temp;
      }
    }
  }
  return arr;
}

var a = [1,35,24,65,2];
console.log(bubble(a));

②插入排序
插入排序思想:和打牌类似,每次从数组中拿一个,和现有的进行从后往前比较,大的话放后面,小的话继续往前比较。
在这里插入图片描述

function insert(arr) {
  // 准备一个新数组,用于存储抓到的牌
  let handle = [];
  // 开始先抓一张牌
  handle.push(arr[0]);
  // 从第二项开始依次抓牌,一直把牌抓完
  for (let i=1; i<arr.length; i++) {
    // A是新拿到的牌
    let A = arr[i];
    // 和handle手里的牌比较(从后往前)
    for(let j=handle.length-1; j>=0; j--) {
      let B = handle[j];
      // 如果新抓的牌比手里的大,放到后边
      if (A > B) {
        handle.splice(j+1, 0, A);
        break;
      }
      // 比到第一项,把新抓的牌放到第一项
      if (j === 0) {
        handle.unshift(A);
      }
    }
  }
  return handle;
}

var a = [1,35,24,35,65,2];
console.log(insert(a));

③快速排序
在这里插入图片描述

function quick(arr) {
  // 4.结束递归(arr中小于等于一项,不用处理)
  if (arr.length <=1) {
    return arr;
  }
  // 1.找到数组的中间项,在原有的数组中把它删除。
  let middleIndex = Math.floor(arr.length/2),
      middleItem = arr.splice(middleIndex, 1)[0];
  // 2.准备左右两个数组,循环剩下数组中的每一项,小的放左数组,反之放右数组。
  let leftArr = [],
      rightArr = [];
  for (let i=0; i<arr.length; i++) {
    let item = arr[i];
    item < middleItem ? leftArr.push(item) : rightArr.push(item);
  }
  // 3.递归方式让左右两边的数组持续处理,一直到左右两边都排好顺序,最后拼接。
  return quick(leftArr).concat(middleItem, quick(rightArr));
}

let a = [2,5,22,11,53,11,41];
console.log(quick(a));

15、两个数组的交集

let arr1 = [1,1,1,1];
let arr2 = [1,1,1,1];
let result = [];

arr1.forEach((item, index) => {
  let n = arr2.indexOf(item);
  if(n >= 0) {
    result.push(item);
    arr2.splice(n, 1);
  }
})

console.log(result)

16、this总结
①普通函数的调用,this指向的是window
②对象方法的调用,this指的是该对象,且是最近的对象
③构造函数的调用,this指的是实例化的新对象
④apply和call调用,this指向参数中的对象
⑤匿名函数的调用,this指向的是全局对象window
⑥定时器中的调用,this指向的是全局变量window

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值