已阅冴羽大佬文章

2021-05-06 14:59:59 浏览数 (1)

https://juejin.cn/post/6958361473953300488

顺便宣传一下:Github来源: | 求星星 ✨ | 欢迎 star,鼓励一下作者。

希望能够帮助更多的小伙伴。加我????即可交流问题(不是大佬,互相学习,创造良好的学习环境)。以下哪些你不懂呢?

  • 哪里不懂的,我们可以下方评论交流

image.png

7cb7de90-8f72-11ea-82a0-b5001424c6ad_76030cd8-ab7c-40e8-bd10-475b50820298_thumb.jpg

扩展的对象功能

image.png

  • 对象类别

image.png

命名一个函数为 createPerson() ,其作用创建了一个对象:

代码语言:javascript复制
function createPerson(name, age) {
 return {
  name: name,
  age: age
 };
}

当对象的一个属性名称与本地变量名相同时,ES6:

代码语言:javascript复制
function createPerson(name, age) {
 return {
  name,
  age
 },
}

当对象字面量中的属性只有名称时, JS 引擎会在周边作用域查找同名变量。若找到,该变量 的值将会被赋给对象字面量的同名属性。

es5写法:

代码语言:javascript复制
var person = {
 name: 'jeskson',
 sayName: function() {
  console.log(this.name);
 }
};

es6写法:

代码语言:javascript复制
var person = {
 name: 'jeskson',
 sayName() {
  console.log(this.name);
 }
};
  • 需计算的属性名

示例:

代码语言:javascript复制
var person = {},
    lastName = "last name";
person["first name"] = "da1";
person[lastName] = "da2";
console.log(person["first name"]); // da1
console.log(person[lastName]); // da2

示例中两个属性名包含了空格,不能使用小数点表示法来引用它们,方括号表示法允许将任意字符串用作属性名。

示例优化:

代码语言:javascript复制
var person = {
 "first name": "jeskson"
};
console.log(person["first name"]); // jeskson
代码语言:javascript复制
var lastName = "last name";
var person = {
 "first name": "da1",
 [lastName]: "da2"
};
console.log(person["first name"]); // "da1"
console.log(person[lastName]); // "da2"

对象字面量内的方括号表明该属性名需要计算,结果是一个字符串

示例:

代码语言:javascript复制
var da = " name";
var pserson = {
 ["first   da ]: "da1",
 ["last"   da ]: "da2"
};
console.log(person["first name"]); // "da1"
console.log(person["last name"]); // "da2"
  • 相等运算符( == )或严格相等运算符( === )
  • ES6: Object.is(),返回true,要求二者类型相同并且值也相等

示例:

代码语言:javascript复制
console.log( 0 == -0); // true
console.log( 0 === -0); // true
console.log(Object.is( 0, -0)); // false

console.log(NaN == NaN); // false
console.log(NaN === NaN); // false
console.log(Object.is(NaN, NaN)); // true

console.log(5 == 5); // true
console.log(5 == "5"); // true
console.log(5 === 5); // true
console.log(5 === "5"); // false
console.log(Object.is(5, 5)); // true
console.log(Object.is(5, "5")); // false

image.png

  • Object.assign()方法

混入( Mixin )是在 JS 中组合对象时最流行的模式。

浅复制,当属性值为对象时,仅复制其引用

代码语言:javascript复制
function mixin(receiver, supplier) {
 Object.keys(supplier).forEach(function(key) {
  receiver[key] = supplier[key];
 });
 return receiver;
}

Object.assign()方法,该方法 接受一个接收者,以及任意数量的供应者,并会返回接收者。

示例:

代码语言:javascript复制
"use strict";
var person = { 
    name: "da1",
    name: "da2" // 在 ES6 严格模式中不会出错
};
console.log(person.name); // "da2"

自有属性的枚举顺序

image.png

修改对象的原型

  1. Object.getPrototypeOf()方法来从任意指定对象中获取其原型
  2. Object.setPrototypeOf()方法修改任意指定对象的原型

示例:

代码语言:javascript复制
let person = {
 getGreeting() {
  return "hello";
 }
};
let dog = {
 getGreeting() {
  return "hai";
 }
};

let friend = Object.create(person);
console.log(friend.getGreeting()); // "hello"
console.log(Object.getPrototypeOf(friend) === person); // true

Object.setPrototypeOf(friend, dog);
console.log(friend.getGreeting()); // "hai"
console.log(Object.getPrototypeOf(friend) === dog); // true

使用 super 引用的简单原型访问

  • Object.getPrototypeOf()方法确保了能调用正确的原型,并在其返回结果上附加了一个字符串。

示例:

代码语言:javascript复制
let person = {
 getGreeting() {
  return "hello"
 }
};
let dog = {
 getGreeting() {
  return "woof";
 }
};
let friend = {
 getGreeting() {
  return Object.getPrototypeOf(this).getGreeting.call(this)   ", hi!";
 }
};
Object.setPrototypeOf(friend, person);
console.log(friend.getGreeting()); // "hello hi!"
console.log(Object.getPrototypeOf(frined) === person); // true
Object.setPrototypeOf(friend,dog);
console.log(friend,getGreeting()); // "woof, hi!"
console.log(Object.getPrototypeOf(friend) === dog); // true
  • call(this),能确保正确设置原型方法内部的this

super是指向当前对象的原型的一个指针

示例:

代码语言:javascript复制
let friend = {
 getGreeting() {
  // Object.getPrototypeOf(this).getGreeting.call(this)
  return super.getGreeting()   ", hi!";
 }
}

使用ES6的super,示例:

代码语言:javascript复制
let person = {
 getGreeting() {
  return "hello";
 }
};
let friend = {
 getGreeting() {
  return super.getGreeting()   ", hi!";
};
Object.setPrototypeOf(friend, person);
let relative = Object.create(friend);
console.log(person.getGreeting()); // "hello"
console.log(friend.getGreeting()); // "hello, hi!"
console.log(relative.getGreeting()); // "hello, hi!"

深入系列专题

1.JavaScript 深入之从原型到原型链

  • JavaScript 深入之从原型到原型链

2.JavaScript 深入之词法作用域和动态作用域

  • JavaScript 深入之词法作用域和动态作用域

3.JavaScript深入之执行上下文栈

  • JavaScript深入之执行上下文栈

4.JavaScript深入之变量对象

  • JavaScript深入之变量对象

5.JavaScript深入之作用域链

  • JavaScript深入之作用域链

6.JavaScript 深入之从 ECMAScript 规范解读 this

  • JavaScript 深入之从 ECMAScript 规范解读 this

7.JavaScript深入之执行上下文

  • JavaScript深入之执行上下文

8.JavaScript深入之闭包

  • JavaScript深入之闭包

9.JavaScript深入之参数按值传递

  • JavaScript深入之参数按值传递

10.JavaScript深入之call和apply的模拟实现

  • JavaScript深入之call和apply的模拟实现

11.JavaScript深入之bind的模拟实现

  • JavaScript深入之bind的模拟实现

12.JavaScript深入之new的模拟实现

  • JavaScript深入之new的模拟实现

13.JavaScript 深入之类数组对象与 arguments

  • JavaScript 深入之类数组对象与 arguments

14.JavaScript深入之创建对象的多种方式以及优缺点

  • JavaScript深入之创建对象的多种方式以及优缺点

面向对象

  • ES6 class语法
  • 三要素
  • UML类图
  • 初始化npm环境
  • 安装webpack
  • 安装webpack-dev-server
  • 安装babel
  • npm init
  • package.json
代码语言:javascript复制
// package.json
// "dev": "wepack --config ./webpack.dev.config.js --mode development"
{
 "name": "design-pattern-text",
 "version": "1.0.0",
 "description": "",
 "main": "index.js",
 "scripts": {
  "test": "echo "Error: no test specified" && exit 1",
  "dev": "webpack-dev-server --config ./webpack.dev.config.js --mode development"
 },
 "author": "",
 "license": "ISC",
 "devDependencies": {
 "html-webpack-plugin": "",
  "webpack": "",
  "webpack-cli": "",
  "webpack-dev-server": ""
 }
}
  • npm install webpack webpack-cli --save-dev
  • webpack.dev.config.js
代码语言:javascript复制
// webpack.dev.config.js
module.exports = {
 entry: './src/index.js‘, 
 output: {
  path: __dirname,
  filename: './release/bundle.js'  
 }
}
  • npm run dev
  • npm install webpack-dev-server html-webpack-plugin --save-dev
代码语言:javascript复制
// webpack.dev.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
 entry: './src/index.js‘, 
 output: {
  path: __dirname,
  filename: './release/bundle.js'  
 },
 
 module: {
  rules: [{
      test: /.js?$/,
      exclude: /(node_modules)/,
      loader: 'babel-loader' // es6语法转es5语法
  }]
 },
 
 glugins: [
  new HtmlWebpackPlugin({
   template: './index.html'
  })
 ],
 devServer: {
  contentBase: path.jon(__dirname,'./release'), // 根目录
  open: true, // 自动打开浏览器
  port: 8080 // 端口
 }
}

解析es6

  • npm install babel-core babel-loader babel-polyfill babel-preset-es2015 babel-preset-latest --save-dev
代码语言:javascript复制
// 根目录.babelrc
{
 "presets": ["es2015", "latest"],
 "plugins": []
}

什么是面向对象

  1. 概念
  2. 三要素:继承,封装,多态
  3. JS的应用举例
  4. 面向对象的意义

示例:

代码语言:javascript复制
// 类
class People {
 constructor(name,age) {
  this.name = name
  this.age = age
 }
 eat() {
  alert(`${this.name} eat something`)
 }
 speak() {
  alert(`My name is ${this.name}, age ${this.age}`)
 }
}
代码语言:javascript复制
// 对象(实例)
// 创建实例
let da1 = new Perople('jeskson1', 12)
da1.eat()
da1.speak()

// 创建实例
let da2 = new People('jeskson2', 13)
da2.eat()
da2.speak()

继承,封装,多态

  1. 继承,子类继承父类
  2. 封装,数据的权限和保密
  3. 多态,同一接口不同实现
代码语言:javascript复制
// 继承
class People {
 constructor(name,age) {
  this.name = name
  this.age = age
 }
 eat() {
  alert(`${this.name} eat something`)
 }
 speak() {
  alert(`My name is ${this.name}, age ${this.age}`)
 }
}

// 子类继承父类
class Student extends People {
 constructor(name, age, number) {
  super(name, age)
  this.number = number
 }
 study() {
  alert(`${this.name} study`)
 }
}
代码语言:javascript复制
// 实例
let jeskson1 = new Student('da1', 10, '01')
jeskson1.study()
console.log(jeskson1.number)
jeskson1.eat()

继承可将共功方法抽离出来,提高复用

  • 封装(public完全开发,protected对子类开放,private对自己开放)
  • 使用typescript
代码语言:javascript复制
// 封装 父类
class People {
 public name
 private age
 protected weight
 constructor(name, age) {
  this.name = name
  this.age = age
  this.weight = 223
 }
 eat() {
  alert(`${this.name} eat something`)
 }
 speak() {
  alert(`My name is ${this.name}, age ${this.age}`)
 }
}
代码语言:javascript复制
// 子类
class Student extends People {
 number
 private grilfriend
 constructor(name, age, number) {
  super(name,age)
  this.number = number
  this.girlfriend = '小舞'
 }
 study() {}
 getWeight() {}
}

减少耦合,不该外露的不外露;利用数据,接口的权限管理;一般_开头的属性是private

  • 多态:同一接口的不同表现
代码语言:javascript复制
// 保持子类的开放性和灵活性
class People {
 constructor(name) {
  this.name = name
 }
 say() {}
}

class A extends People {
 constructor(name) {
  super(name)
 }
 say() {console.log('A dadaqianduan.cn')}
}

class B extends People {
 constructor(name) {
  super(name)
 }
 say() {console.log('B dadaqianduan.cn')}
}

let a = new A('a')
a.say()

let b = new b('b')
b.say()
  • 面向对象:顺序,判断,循环,结构化

UML类图

image.png

image.png

15.JavaScript深入之继承的多种方式和优缺点

  • JavaScript深入之继承的多种方式和优缺点

JavaScript专题

1.JavaScript专题之跟着underscore学防抖

  • JavaScript专题之跟着underscore学防抖

在前端开发中会遇到一些频繁的事件触发,如:

  1. window 的 resize、scroll
  2. mousedown、mousemove
  3. keyup、keydown

2.JavaScript专题之跟着 underscore 学节流

  • JavaScript专题之跟着 underscore 学节流

3.JavaScript专题之数组去重

  • JavaScript专题之数组去重

4.JavaScript专题之类型判断(上)

  • JavaScript专题之类型判断(上)

5.JavaScript专题之类型判断(下)

  • JavaScript专题之类型判断(下)

在 ES6 前,JavaScript 共六种数据类型

代码语言:javascript复制
Undefined、Null、Boolean、Number、String、Object

image.png

代码语言:javascript复制
[object Number]
[object String]
[object Boolean]
[object Undefined]
[object Null]
[object Object]
[object Array]
[object Date]
[object Error]
[object RegExp]
[object Function]
[object Math]
[object JSON]
[object Arguments]
代码语言:javascript复制
// jquery:
type: function(obj) {
 if(obj == null) {
  return obj   "";
 }
 
 return typeof obj === "object" || typeof obj === "function" ? class2type[toString.call(obj) ] || "object" : typeof obj;
}

6.JavaScript专题之深浅拷贝

  • JavaScript专题之深浅拷贝
  • 浅拷贝:复制引用方法,两者都会发生变化
  • 深拷贝:即使嵌套了对象,也相互分离,互不影响

对象:对象通过两种形式定义:1,声明形式;2,构造形式

代码语言:javascript复制
var myObj = {...};

var myObj = new Object();

类型:对象是JavaScript的基础。6种主要类型:

  1. string
  2. number
  3. boolean
  4. null
  5. undefined
  6. object

简单基本类型,string, number, boolean, null, undefined本身不是对象,null执行typeof null时会返回字符串object,实际上,null本身是基本类型。

函数就是对象的一个子类型,JavaScript中的函数是“一等公民”,因为它们本质上和普通的对象一样,所以可以像操作其他对象一样操作函数。

常见内置对象:

  1. String
  2. Number
  3. Boolean
  4. Object
  5. Function
  6. Array
  7. Date
  8. RegExp
  9. Error

ES6定义了Object.assign(...)方法来实现浅复制。

Object.assign(...)方法的第一个参数是目标对象,之后还可以跟一个或多个源对象。它会遍历一个或多个源对象的所有可枚举的自有键并把它们复制到目标对象,最后返回目标对象

示例:

代码语言:javascript复制
function anotherFunction() { /*..*/ }
var anotherObject = {
 c: true
};
var anotherArray = [];
var myObject = {
 a: 2, 
 b: anotherObject, // 引用,不是复本!
 c: anotherArray, // 另一个引用!
 d: anotherFunction
};
anotherArray.push( anotherObject, myObject );

var newObj = Object.assign( {}, myObject );
newObj.a; // 2
newObj.b === anotherObject; // true
newObj.c === anotherArray; // true
newObj.d === anotherFunction; // true

属性描述符

代码语言:javascript复制
var myObject = {
 a: 2
};
Object.getOwnPropertyDescriptor(myObject, "a");
// {
//  value: 2,
//  writable: true,
//  enumerable: true,
//  configurable: true,
// }
// writable可写
// enumerable可枚举
// configurable可配置
代码语言:javascript复制
var myObject = {};
Object.defineProperty(myObject, "a", {
 value: 2,
 writable: true,
 configurable: true,
 enumerable: true
});
myObject.a; // 2

使用defineProperty(...)myObject添加了一个普通的属性并显式指定了一些特性。

  • writable决定是否可以修改属性的值

示例:

代码语言:javascript复制
var myObject = {};

Object.defineProperty(myObject,"a",{
 value: 2,
 writable: false, // 不可写
 configurable: true,
 enumerable: true
});

myObject.a = 3;
myObject.a; // 2
  • configurable,只要属性是可配置的,就可以使用defineProperty(...)方法来修改属性描述符。
代码语言:javascript复制
var myObject = {
 a: 2
};
myObject.a = 3;
myObject.a; // 3

Object.defineProperty(myObject, "a", {
 value: 4,
 writable: true,
 configurable: false, // 不可配置
 enumerable: true
});

myObject.a; // 4
myObject.a = 5;
myObject.a; //5

Object.defineProperty(myObject, "a", {
 value: 6,
 wirtable: true,
 configurable: true,
 enumerable: true
}); // TypeError
  • enumerable,控制的是属性是否会出现在对象的属性枚举中。
代码语言:javascript复制
// 创建一个真正的常量属性,不可修改,重定义,删除
var myObject = {};
Object.defineProperty(myObject, "a", {
 value: 1,
 writable: false,
 configurable: false
});
代码语言:javascript复制
// 禁止扩展,使用Object.preventExtensions(...)
// 禁 止 一 个 对 象 添 加 新 属 性 并 且 保 留 已 有 属 性
var myObject = {
 a: 2
};
Object.preventExtensions(myObject);
myObject.b = 3;
myObject.b; // undefined
  • 密封:Object.seal(...)会创建一个“密封”的对象,这个方法实际上会在一个现有对象上调用Object.preventExtensions(...)并把所有现有属性标记为configurable:false(虽然可以 修改属性的值)。
  • 冻结:Object.freeze(..) 会创建一个冻结对象,这个方法实际上会在一个现有对象上调用 Object.seal(..) 并把所有“数据访问”属性标记为 writable:false,这样就无法修改它们的值。

getter,setter

  • getter是一个隐藏函数,会在获取属性值时调用
  • setter是一个隐藏 函数,会在设置属性值时调用

存在性

示例:

代码语言:javascript复制
var myObject = {
 a: 2
};
("a" in myObject); // true
("b" in myObject); // false
myObject.hasOwnProperty("a"); // true
myObject.hasOwnProperty("b"); // false
  • in操作符会检查属性是否在对象及其[[Prototype]]原型链中
  • hasOwnProperty(...)只会检查属性是否在myObject对象中,不会检查[[Prototype]]

枚举

示例:

代码语言:javascript复制
var myObject = {};
Object.defineProperty(
 myObject,
 "a",
 { enumerable: true, value: 2 }
);
Object.defineProperty(
 myObject,
 "b",
 { enumerable: false, value: 3}
);

myObject.b; // 3
("b" in myObject); // true
myObject.hasOwnProperty("b"); // true

for(var k in myObject) {
 console.log(k, myObject[k]);
}
// "a" 2

检查属性是否可枚举:

代码语言:javascript复制
var myObject = {};
Object.defineProperty(
 myObject,
 "a",
 {enumerable: true, value: 2}
);
Object.defineProperty(
 myObject,
 "b",
 {enumerable: false, value:3}
);

myObject.propertyIsEnumerable("a"); // true
myObject.propertyIsEnumerable("b"); // false
Object.keys(myObject); // ["a"]
Object.getOwnPeropertyNames(myObject); // ["a", "b"]
  • propertyIsEnumerable(...)会检查给定的属性名是否直接存在于对象中,并满足enumerable: true
  • Object.keys(...)会返回一个数组,包含所有可枚举属性
  • Object.getOwnPropertyNames(...)会返回一个数组,包含所有属性,无论它们是否可枚举
  • inhasOwnProperty(...)的区别在于是否查找[[Prototype]]
  • Object.keys(...)Object.getOwnPropertyNames(...)都只会查找对象直接包含的属性

可以使用 Object.preventExtensions(..)、Object.seal(..) 和 Object.freeze(..) 来设置对象的不可变性级别。

遍历

ES6方法:forEach(),every(),some()

  • forEach(...)会遍历数组中的所有值并忽略回调函数的返回值
  • every(...)会一直运行直到回调函数返回false
  • some(...)会一直运行直到回调函数返回true

ES6中新增加了一种用来遍历数组的for...of循环语法:

示例:

代码语言:javascript复制
var myArray = [1,2,3];
for(var v of myArray) {
 console.log(v);
}
// 1
// 2
// 3

7.JavaScript 专题之从零实现 jQuery 的 extend

  • JavaScript 专题之从零实现 jQuery 的 extend

8.JavaScript 专题之如何求数组的最大值和最小值

  • JavaScript 专题之如何求数组的最大值和最小值

image.png

代码语言:javascript复制
var arr = [ 2,34,5,8];
function max(prev, next) {
 return Math.max(prev, next);
}
console.log(arr.reduce(max));
代码语言:javascript复制
arr.sort(function(a,b){return a-b;});
console.log(arr[arr.length-1]);
代码语言:javascript复制
var max = eval("Math.max("   arr   ")");

Math.max.applly(null, arr);

Math.max(...arr);

9.JavaScript 专题之数组扁平化

  • JavaScript 专题之数组扁平化

image.png

10.JavaScript专题之学underscore在数组中查找指定元素

  • JavaScript专题之学underscore在数组中查找指定元素

11.JavaScript专题之jQuery通用遍历方法each的实现

  • JavaScript专题之jQuery通用遍历方法each的实现

12.JavaScript 专题之如何判断两个对象相等

  • JavaScript 专题之如何判断两个对象相等

构造函数,类的继承,混入

构造函数:类实例是由一个特殊的类方法构造的,这个方法名通常和类名相同,称为构造函数。这个方法的任务就是 初始化实例需要的所有信息。

类的继承:在面向类的语言中,你可以先定义一个类,然后定义一个继承前者的类。后者通常被称为“子类”,前者通常被称为“父类”。

混入:在继承或者实例化时,JavaScript的对象机制并不会自动执行复制行为。简单来说,JavaScript中只有对象,并不存在可以被实例化的“类”复制行为,在JavaScript中模拟类的复制行为,这个方法就是混入。

两种类型的混入:1,显式;2,隐式

示例:

代码语言:javascript复制
function mixin( sourceObj, targetObj ) {
 for (var key in sourceObj) {
  // 只会在不存在的情况下复制
  if(!(key in targetObj)) {
   targetObj[key] = sourceObj[key];
  }
 }
 return targetObj;
}

var Vehicle = {
 engines: 1,
 ignition: function() {
  console.log("truing on my engine");
 },
 drive: function() {
  this.ignition();
  console.log("steering and moving forward!");
 }
};
var Car = mixin(Vehicle, {
 wheels: 4,
 drive: function() {
  Vehicle.drive.call(this); // 多态
  console.log(
   "Rolling on all"   this.wheels   "wheels"
  };
 }
});

寄生继承

显式混入模式的一种变体被称为“寄生继承”,它既是显式的又是隐式的。

示例:

代码语言:javascript复制
function Vehicle() {
 this.engines = 1;
}
Vehicle.prototype.ignition = function() {
 console.log("turning on my engine");
};
Vehicle.prototype.drive = function() {
 this.ignition();
 console.log("steering and moving forward");
};
// 寄生类Car
function Car() {
 var car = new Vehicle();
 car.wheels = 4;
 var vehDrive = car.drive;
 // 重写
 car.drive = function() {
  vehDrive.call(this);
  console.log("rolling on all"   this.wheels   "wheels");
  return car;
 }
 myCar.drive();

隐式混入

示例:

代码语言:javascript复制
var Something = {
 cool: function() {
  this.greeting = "hello world";
  this.count = this.count ? this.count   1 : 1;
 }
};
Something.cool();
Something.greeting; / "hello world"
Something.count; // 1

var Another = {
 cool: function() {
  // 隐式把Something 混入Another
  Something.cool.call(this);
 }
};

Another.cool();
Another.greeting; // "hello world"
Another.count; // 1 (count不是共享状态)

W3C标准事件流

W3C标准事件流: 包含3个阶段,捕获阶段,目标阶段,冒泡阶段。

  1. 在捕获阶段,事件对象通过目标的祖先从窗口传播到目标的父级。
  2. 在目标阶段,事件对象到达事件对象的事件目标。
  3. 在冒泡阶段,事件对象以相反的顺序通过目标的祖先传播,从目标的父级开始,到窗口结束。
  4. 先从顶层对象 window 开始一路向下捕获,直到达到目标元素,其后进入目标阶段。
  5. 目标元素 div 接收到事件后开始冒泡到顶层对象 window
  6. 单击了<div>元素,则首先会进行事件捕获,此时事件按 window→document→<html>→<body> 的顺序进行传播
  7. 当事件对象传到 <div> 时进入目标阶段,接着事件对象又从目标对象传到 body,从而进入事件的冒泡阶段
  8. 事件对象按 <body>→<html>→document→window 的顺序传播事件。

image.png

13.JavaScript 专题之函数柯里化

  • JavaScript 专题之函数柯里化

14.JavaScript 专题之惰性函数

  • JavaScript 专题之惰性函数

15.JavaScript专题之函数组合

  • JavaScript专题之函数组合

16.JavaScript 专题之函数记忆

  • JavaScript 专题之函数记忆

17.JavaScript专题之递归

  • JavaScript专题之递归

18.JavaScript专题之乱序

  • JavaScript专题之乱序

19.JavaScript专题之解读 v8 排序源码

  • JavaScript专题之解读 v8 排序源码

原型

JavaScript中的对象有一个特殊的[[Prototype]]内置属性,其实就是对于其他对象的引用。几乎所有的对象在创建时[[Prototype]]属性都会被赋予一个非空的值。

  • 使用in操作符来检查属性在对象中是否存在时,同样会查找对象的整条原型链。

示例:

代码语言:javascript复制
var anotherObject = {
 a: 2
};
// 创建一个关联到anotherObject的对象
var myObject = Object.create(anthorObject);
for(var k in myObject) {
 console.log("found:"   k);
}
// found:a
("a" in myObject); // true

Object.prototype

所有普通的 [[Prototype]] 链最终都会指向内置的 Object.prototype

所有的函数默认都会拥有一个名为 prototype 的公有并且不可枚举的属性,它会指向另一个对象

代码语言:javascript复制
function Foo() {
 // ...
}
Foo.prototype; // { }

这个对象通常被称为 Foo 的原型,因为我们通过名为 Foo.prototype 的属性引用来访问它

代码语言:javascript复制
var a = new Foo();

Object.getPrototypeOf( a ) === Foo.prototype; // true

new Foo() 会生成一个新对象,这个新对象的内部链接 [[Prototype]] 关联 的是 Foo.prototype 对象

Object.create(...)会创建一个新对象并把它关联到我们指定的对象,Object.create(null)会创建一个拥有空链接的对象,这个对象无法进行委托。

由于这个对象没有原型链,所以instanceof操作符无法进行判断,因此总是会返回false。这些特殊的空[[Prototype]]对象通常被称为“字典”,它们完全不会受到原型链的干扰,非常适合用来存储数据。

示例:

代码语言:javascript复制
// polyfill 代码
if(!Object.create) {
 Object.create = function(o) {
  function F(){}
  F.prototype = o;
  return new F(); // 构造一个新对象进行关联
 };
}

示例:

代码语言:javascript复制
var anotherObject = {
 a: 2
};

var myObject = Object.create( anotherObject, {
 b: {
  enumerable: false,
  writable: true,
  configurable: false,
  value: 3
 },
 c: {
  enumerable: true,
  writable: false,
  configurable: false,
  value: 4
 }
});
myObject.hasOwnProperty( "a" ); // false
myObject.hasOwnProperty( "b" ); // true
myObject.hasOwnProperty( "c" ); // true
myObject.a; // 2
myObject.b; // 3
myObject.c; // 4

image.png

示例:

代码语言:javascript复制
var anotherObject = {
 cool: function() {
  console.log("cool");
 }
};
var myObject = Object.create(anotherObject);
myObject.doCool = function() {
 this.cool(); // 内部委托
};
myObject.doCool(); // "cool"

使用new调用函数时会把新对象的.prototype属性关联到“其他对象”。带new的函数调用通常被称为“构造函数调用”。

对象之间是通过内部的[[Prototype]]链关联的。

示例:

代码语言:javascript复制
class Task {
 id;
 // 构造函数Task()
 Task(ID) { id = ID; }
 outputTask() {output(id); }
}

class XYZ inherits Task {
 label;
 // 构造函数 XYZ()
 XYZ(ID,Label) { super( ID ); label = Label; }
 outputTask() { super(); output( label ); }
}

class ABC inherits Task {
 // ...
}

推荐代码:

代码语言:javascript复制
Task = {
 setID: function(ID) { this.id = ID; },
 outputID: function() { console.log( this.id ); }
};

// 让 XYZ 委托 Task 
XYZ = Object.create( Task );

XYZ.prepareTask = function(ID,Label) {
this.setID( ID );
this.label = Label; 
};


XYZ.outputTaskDetails = function() {
this.outputID(); console.log( this.label ); 
};
// ABC = Object.create( Task );

面向对象:

代码语言:javascript复制
function Foo(who) {
this.me = who; 
}

Foo.prototype.identify = function() {
return "I am "   this.me; 
};

function Bar(who) { 
Foo.call( this, who ); 
}

Bar.prototype = Object.create( Foo.prototype ); 

Bar.prototype.speak = function() { 
alert( "Hello, "   this.identify()   "." ); 
};

var b1 = new Bar( "b1" );
var b2 = new Bar( "b2" ); 

b1.speak();
b2.speak();

对象关联:

代码语言:javascript复制
Foo = { 

init: function(who) {
this.me = who; 
},

identify: function() {
return "I am "   this.me; 
} 

};


Bar = Object.create( Foo ); 
Bar.speak = function() { 
alert( "Hello, "   this.identify()   "." ); 
};

var b1 = Object.create( Bar ); 
b1.init( "b1" ); 
var b2 = Object.create( Bar );
b2.init( "b2" ); 
b1.speak(); 
b2.speak();

更简洁的设计

示例:

代码语言:javascript复制
// 父类
function Controller() {
 this.errors = [];
}
Controller.ptototype.showDialog(title,msg) {
 // 给用户显示标题和消息
};
Controller.prototype.success = function(msg) {
 this.showDialog("Success", msg);
};
Controller.prototype.failure = function(err) {
 this.errors.push(err);
 this.showDialog("Error", err);
};

// 子类
function LoginController() {
 Controller.call(this);
}
// 把子类关联到父类
LoginController.prototype = Object.create(Controller.prototype);

LoginController.prototype.getUser = function() {
 return document.getElementById("login_username").value;
};
LoginController.prototype.getPassword = function() {
 return docuemnt.getElementById("login_password").value;
};

LoginController.prototype.validateEntry = function(user, pw) {
 user = user || this.getUser();
 pw = pw || this.getPassword();
 
 if( !(user && pw) ) {
  return this.failure(
   "please enter a username & password!"
   );
 }
 else if( user.length<5) {
  return this.failuer(
   "password must be 5  characters"
   );
 }
 // 如果执行到这里表示通过验证
 return true;
};
// 重写failure()
LoginController.prototype.failure = function(err) {
 // "super" 调用
 Controller.prototype.failure.call(
  this,
  "Login invalid"   err
  );
 };
 // 子类
 function AuthController(login) {
  Controller.call(this);
  // 合成
  this.login = login;
 }
// 把子类关联到父类
AuthController.prototype = 
 Object.crate(Controller.prototype);

AuthController.prototype.server = function(url, data) {
 return $.ajax({
  url: url,
  data: data
 });
};
AuthController.prototype.checkAuth = function() {
 var user = this.login.getUser();
 var pw = this.login.getPassword();
 
 if (this.login.validateEntry(user.pw)) {
 this.server( "/check-auth",{
 user: user,
 pw: pw
 } ) .then( this.success.bind( this ) ) 
 .fail( this.failure.bind( this ) );
 }
};

// 重写基础的 success() 
AuthController.prototype.success = function() { 
//“super”调用 
Controller.prototype.success.call( this, "Authenticated!" ); 
};

// 重写基础的 failure() 
AuthController.prototype.failure = function(err) { 
//“super”调用 
Controller.prototype.failure.call(
this, 
"Auth Failed: "   err 
); 
};

var auth = new AuthController();
auth.checkAuth( 
// 除了继承,我们还需要合成
new LoginController() 
);

对象关联:

代码语言:javascript复制
var LoginController = { 
errors: [], 

getUser: function() {
return document.getElementById( "login_username" ).value; 
},

getPassword: function() {
return document.getElementById( "login_password" ).value; 
},

validateEntry: function(user,pw) { 
user = user || this.getUser(); 
pw = pw || this.getPassword();

if (!(user && pw)) {
return this.failure( "Please enter a username & password!" ); 
}
else if (user.length < 5) {
return this.failure( "Password must be 5  characters!" ); 
}
// 如果执行到这里说明通过验证

return true; 
},

showDialog: function(title,msg) { 
// 给用户显示标题和消息 
},

failure: function(err) {
this.errors.push( err );
this.showDialog( "Error", "Login invalid: "   err ); 
} 
};

// 让 AuthController 委托 LoginController

var AuthController = Object.create( LoginController ); 

AuthController.errors = []; 

AuthController.checkAuth = function() {
var user = this.getUser();
var pw = this.getPassword(); 
if (this.validateEntry( user, pw )) {
this.server( "/check-auth",{ 
user: user,
pw: pw 
} ) .then( this.accepted.bind( this ) ) 
.fail( this.rejected.bind( this ) ); 
} 
};

AuthController.server = function(url,data) {
return $.ajax( { 
url: url, 
data: data 
} ); 
};

AuthController.accepted = function() {
this.showDialog( "Success", "Authenticated!" ) 
};

AuthController.rejected = function(err) {
this.failure( "Auth Failed: "   err ); 
};

更好的语法

示例:

代码语言:javascript复制
class Foo { methodName() { /* .. */ } }

0 人点赞