0、一些概念
(1)JavaScript 中的所有事物都是对象:字符串、数值、数组、函数;此外,JavaScript 允许自定义对象。对象只是一种特殊的数据,对象拥有属性和方法;即对象只是带有属性和方法的特殊数据类型。
- 布尔型可以是一个对象。数字型可以是一个对象。字符串也可以是一个对象。日期是一个对象。数学和正则表达式也是对象。数组是一个对象。甚至函数也可以是对象。
- 请不要把字符串、数值和布尔值声明为对象!如果通过关键词 “new” 来声明 JavaScript 变量,则该变量会被创建为对象:
var x = new String(); // 把 x 声明为 String 对象
var y = new Number(); // 把 y 声明为 Number 对象
var z = new Boolean(); // 把 z 声明为 Boolean 对象
请避免字符串、数值或逻辑对象,他们会增加代码的复杂性并降低执行速度。
(2)JavaScript 是面向对象的语言,但 JavaScript 不使用类。在 JavaScript 中,不会创建类,也不会通过类来创建对象(就像在其他面向对象的语言中那样)。JavaScript 基于 prototype(原型),而不是基于类的。(函数 + prototype 机制)
- 基于类的是创建模型,基于原型的是经验模型,前者是现有马这个概念,才有胯下之马这个实体,后者是先有胯下之马这个实体,才有马这个概念。
- 在思考面向原型的时候,类的概念有是可以的,因为本质上对象的原型就是类。但是,类构造出对象的这个过程却要不得,面向原型里,是由具体对象推导出原型来的。
- 访问对象的属性或者方法时,会访问其__proto__以及__proto__的__proto__,直到找到这个属性或者方法为止,而函数的prototype属性执行后会返回执行结果的__proto__。
1、apply() 方法与 call() 方法
apply() 方法能劫持另外一个对象的方法,继承另外一个对象的属性。
var person = {
fullName: function (city, country) {
return this.firstName + " " + this.lastName + "," + city + "," + country;
}
}
var person1 = {
firstName: "Bill",
lastName: "Gates"
}
var x = person.fullName.apply(person1, ["Seatle", "USA"]);
// var x = person.fullName.call(person1, "Seatle", "USA");
// 应用输出:
Bill Gates, Seatle, USA
2、函数节流(throttle)与函数防抖(debounce)
函数节流:限制一个函数在一定时间内只能执行一次;即在一定时间之内,限制一个动作只执行一次。
// 时间戳方案
function throttle(fn, wait) {
let pre = Date.now();
return function () {
let context = this;
let args = arguments;
let now = Date.now();
if (now - pre >= wait) {
fn.apply(context, args);// args 即上面的 pre,this 是为了能持有 args
pre = Date.now();
}
}
}
window.addEventListener("mousemove", throttle(fu, 1000));
函数防抖:短时间内多次触发同一事件,只执行最后一次,或者只执行最开始的一次,中间的不执行。
/**
* @desc 函数防抖
* @param func 目标函数
* @param wait 延迟执行毫秒数
* @param immediate true - 立即执行, false - 延迟执行
*/
function debounce(func, wait, immediate) {
let timer;
return function () {
let context = this;
let args = arguments;
if (timer) clearTimeout(timer);
if (immediate) {
let callNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait);
if (callNow) func.apply(context, args);
} else {
timer = setTimeout(() => {
func.apply(context, args);
}, wait)
}
}
}
3、数组去重的n种方式
利用数组 filter 方法过滤:
Array.prototype.unique = function unique() {
var res = this.filter(function (item, index, array) {
return array.indexOf(item) === index;
});
return res;
}
利用对象属性的唯一性:
Array.prototype.unique = function () {
var temp = [], hash = {}; // hash 作为哈希表
for (var i = 0; i < this.length; i++) {
if (!hash[this[i]]) { // 如果哈希表中没有当前项
hash[this[i]] = true;
temp.push(this[i])
}
}
return temp;
}
利用 ES6 set 数据结构:
Array.prototype.unique = function () {
return Array.from(new Set(this));
}
运行速度差异不大,不过鉴于 set 是 ES6 新加内容,在实际开发环境中,推荐使用稳定性和速度都比较不错的前两个方法。
4、解构赋值【ECMAScript 6.0 规范(ES6)】
1、使用数组成员对变量赋值时,优先使用解构赋值:
const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;
2、函数的参数如果是对象的成员,优先使用解构赋值:
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
}
// good
function getFullName(obj) {
const { firstName, lastName } = obj;
}
// best
function getFullName({ firstName, lastName }) {
}
3、TODO: 如果函数返回多个值,优先使用对象的解构赋值,而不是数组的解构赋值。这样便于以后添加返回值,以及更改返回值的顺序:
// bad
function processInput(input) {
return [left, right, top, bottom];
}
// good
function processInput(input) {
return { left, right, top, bottom };
}
const { left, right } = processInput(input);
for (const { name, age } of persons) {
console.log(name);
console.log(age);
}
5、get 和 set 方法
getter 是获取属性值,setter 是设置属性值,getter 不带任何参数,setter 设置键值,值以参数的形式传递,在 setter 函数体中,一切的 return 都是无效的,当只有 setter 函数时,那这个属性是只写的,当只有 getter 函数时,那这个属性是只读的,同时有 setter、getter 函数,这个属性可读可写。
let o = {
get x() {
return 7;
},
set x(val) {
console.info("不能设置x的值");
}
}
o.x //7 读取x值的时候,会使用get方法
o.x = 9 //不能设置x的值 赋值x时,会使用set方法
let p = {
get() {
return this._p;
},
set(value) {
value = value === undefined ? 0 : value;
this._p = value;
// 悄悄干点其它的操作
this.xxxxx(value);
}
}
参考:
苏格拉没有底:https://segmentfault.com/u/sugelameiyoudi_5a3a4031b8a3f/articles