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()
,其作用创建了一个对象:
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
修改对象的原型
Object.getPrototypeOf()
方法来从任意指定对象中获取其原型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
,示例:
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
// 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
// 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
// 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
// 根目录.babelrc
{
"presets": ["es2015", "latest"],
"plugins": []
}
什么是面向对象
- 概念
- 三要素:继承,封装,多态
- JS的应用举例
- 面向对象的意义
示例:
代码语言: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()
继承,封装,多态
- 继承,子类继承父类
- 封装,数据的权限和保密
- 多态,同一接口不同实现
// 继承
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
// 封装 父类
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
- 多态:同一接口的不同表现
// 保持子类的开放性和灵活性
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学防抖
在前端开发中会遇到一些频繁的事件触发,如:
window 的 resize、scroll
mousedown、mousemove
keyup、keydown
2.JavaScript专题之跟着 underscore 学节流
- JavaScript专题之跟着 underscore 学节流
3.JavaScript专题之数组去重
- JavaScript专题之数组去重
4.JavaScript专题之类型判断(上)
- JavaScript专题之类型判断(上)
5.JavaScript专题之类型判断(下)
- JavaScript专题之类型判断(下)
代码语言:javascript复制在 ES6 前,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专题之深浅拷贝
- 浅拷贝:复制引用方法,两者都会发生变化
- 深拷贝:即使嵌套了对象,也相互分离,互不影响
代码语言:javascript复制对象:对象通过两种形式定义:1,声明形式;2,构造形式
var myObj = {...};
var myObj = new Object();
类型:对象是JavaScript的基础。6种主要类型:
string
number
boolean
null
undefined
object
简单基本类型,
string, number, boolean, null, undefined
本身不是对象,null
执行typeof null
时会返回字符串object
,实际上,null
本身是基本类型。
函数就是对象的一个子类型,JavaScript中的函数是“一等公民”,因为它们本质上和普通的对象一样,所以可以像操作其他对象一样操作函数。
常见内置对象:
String
Number
Boolean
Object
Function
Array
Date
RegExp
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(...)
方法来修改属性描述符。
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
,控制的是属性是否会出现在对象的属性枚举中。
// 创建一个真正的常量属性,不可修改,重定义,删除
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(...)
会返回一个数组,包含所有属性,无论它们是否可枚举in
和hasOwnProperty(...)
的区别在于是否查找[[Prototype]]
链Object.keys(...)
和Object.getOwnPropertyNames(...)
都只会查找对象直接包含的属性
可以使用 Object.preventExtensions(..)、Object.seal(..) 和 Object.freeze(..)
来设置对象的不可变性级别。
遍历
ES6
方法:forEach()
,every()
,some()
forEach(...)
会遍历数组中的所有值并忽略回调函数的返回值every(...)
会一直运行直到回调函数返回falsesome(...)
会一直运行直到回调函数返回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个阶段,捕获阶段,目标阶段,冒泡阶段。
- 在捕获阶段,事件对象通过目标的祖先从窗口传播到目标的父级。
- 在目标阶段,事件对象到达事件对象的事件目标。
- 在冒泡阶段,事件对象以相反的顺序通过目标的祖先传播,从目标的父级开始,到窗口结束。
- 先从顶层对象
window
开始一路向下捕获,直到达到目标元素,其后进入目标阶段。 - 目标元素 div 接收到事件后开始冒泡到顶层对象
window
。 - 单击了
<div>
元素,则首先会进行事件捕获,此时事件按window→document→<html>→<body>
的顺序进行传播 - 当事件对象传到
<div>
时进入目标阶段,接着事件对象又从目标对象传到body
,从而进入事件的冒泡阶段 - 事件对象按
<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
的公有并且不可枚举的属性,它会指向另一个对象
function Foo() {
// ...
}
Foo.prototype; // { }
代码语言:javascript复制这个对象通常被称为 Foo 的原型,因为我们通过名为 Foo.prototype 的属性引用来访问它
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() { /* .. */ } }