js的中对象高级部分理解vue的双向数据绑定,以及创建对象 --- 设计模式
对象
含义:无序属性的集合,其属性可以包含基本值、对象或者函数。”严格来讲,对象是一组没有特定顺序的值,那么它肯定是一个引用数据类型。
对象的属性类型
es5定义了只有内部才用的特性(attribute)时,描述了属性(property原型)的各种特征。 es5 这些特性是为了实现JavaScript 引擎用的,因此在 Js 中不能直接访问它们;
js属性有两种: 数据属性和访问器属性
1. 数据属性( 4个特性)
- [Configurable] Boolean 能否通过 delete 删除属性(默认true)
- [Enumerable] Boolean 能否通过 for-in 循环返回属性(默认true)
- [Writable] Boolean 能否修改属性的值(默认true)
- [Value] Boolean 包含这个属性的数据值(默认值是undefind)
使用 Object.defineProperty() 方法,修改属性的默认行为 ;
接受参数(属性所在的对象、属性的名字,一个描述符对象)描述符(descriptor)对象的属 性必须是:
configurable、
enumerable、
writable 和
value 设置其中的一或多个值
ps1: 多次调用 Object.defineProperty()方法修改同一个属性,在把 configurable 特性设置为 false
之后就会有限制了
var people = {};
Object.defineProperty(people, 'name', {
configurable: false
});
people.name = '12';
console.log(people); // undefined
ps2: 调用 Object.defineProperty()设置空对象的属性时,如果不指定,configurable、enumerable
和 writable 特性的默认值都是 false
console.log(Object.prototype);
var people = {};
Object.defineProperty(people, 'name', {});
people.name = '12';
console.log(people); // undefind
访问器属性(包含一对儿 getter 和 setter 函数)
在读取访问器属性时,会调用 getter 函数,这个函数负责返回有效的值
写入访问器属性时,会调用 setter 函数并传入新值,这个函数负责决定如何处理数据
有4个特性
[Configurable] Boolean 能否通过 delete 删除属性(默认true)
[Enumerable] Boolean 能否通过 for-in 循环返回属性(默认true)
[Get]: 在读取属性时调用的函数。默认值为 undefined
[Set]:在写入属性时调用的函数。默认值为 undefined
访问器属性不能直接创建, 必须使用 Object.defineProperty() 来定义
var book = {
_year: 200,
edit: 1
};
Object.defineProperty(book, 'year', {
get: function() {
return this._year;
},
set(newValue) {
if (newValue > 2004) {
this._year = newValue;
this.edit = newValue - 2004;
}
}
});
book.year = 2008;
console.log(book);
Vue的双向数据绑定 就是 Object.defineProperty( ) 方法搞的
来简单说下vue的原理
- 通过document.createDocumentFragment()创建虚拟DOM
- 会通过Object.defineProperty定义的数据拦截,截取到数据的变化。
- 通过订阅—发布者模式,触发Watcher(观察者),从而改变虚拟dom的中的具体数据
- 通过更新虚拟dom的元素值,改变最终渲染dom元素的值,完成双向绑定
关于 document.createDocumentFragment() 方法
创建对象 - 设计模式
参考红皮书啊,这里不做任何解释, 就是让你看看其实你平时在写代码的时候也用到了设计模式,还有一面试 人家问点设计模式就不知道是啥!
工厂模式
说白了,就跟你自己封装方法一样,最后返回一个对象,可能你都已经写过这种模式了
function createPeople(name, age) {
var o = new Object();
o.name = name;
a.age = age;
o.say = function() {
return this.name;
};
return o;
}
// 实例化对象
var p1 = new createPeople('里斯', 12);
var p2 = new createPeople('大钟', 16);
构造函数模式
构造函数是定义在 Global 对象(在浏览器中是 window 对象)中的,经常写吧,如果不知道,那es6 的class 你见过吧!
function Animal(name, age) {
this.name = name;
this.age = age;
this.say = function() {
return this.name + ' : ' + this.age;
};
}
// ps 一个面试题
// 要创建 Animal 的新实例,必须使用 new 操作符。调用构造函数实际上会经历以下 4 个步骤:
// (1) 创建一个新对象;
// (2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
// (3) 执行构造函数中的代码(为这个新对象添加属性);
// (4) 返回新对象
/**
* 构造函数当作函数
* 1. 构造函数与其他函数的唯一区别,就在于调用它们的方式不同
* 2. 任何函数,只要通过 new 操作符来调用,那它就可以作为构造函数
* 3. 任何函数,不通过 new 操作符来调用,那它跟普通函数也不会有什么两样
*/
// 构造函数实例化调用 => 使用dog
var dog = new Animal('狗', 12);
dog.say();
// 普通调用 => 使用window
Animal('猫', 2);
console.log(window.say());
// 在另一个对象的作用域中调用
var s = {};
Animal.call(s, '狐狸', 2);
console.log(s.say());
寄生构造函数模式
function SpecialArray() {
//创建数组
var values = new Array();
values.push.apply(values, arguments);
//添加方法
values.toPipedString = function() {
return this.join('|');
};
//返回数组
return values;
}
var colors = new SpecialArray('red', 'blue', 'green');
console.log(colors.toPipedString()); //"red|blue|green"
原型模式
- 创建的每个函数都有一个 prototype(原型)属性,
- 这个属性是一个指针,指向一个对象,
- 而这个对象的 包含 所有实例共享的属性和方法
function Person() {}
Person.prototype.name = 'jooker';
Person.prototype.age = 19;
Person.prototype.say = function() {
return this.name;
};
var p = new Person();
console.log(p.__proto__=== Person.prototype)
console.log(p.constructor === Person)
console.log(Person.prototype.constructor === p.constructor)
组合使用构造函数模式和原型模式
function Person(name) {
this.name = name;
this.friends = ['Shelby', 'Court'];
}
Person.prototype = {
constructor: Person,
sayName: function() {
return this.name;
}
};
var person1 = new Person('Nicholas');
var person2 = new Person('Greg');
person1.friends.push('Van');
console.log(person1.friends); //"Shelby,Count,Van"
console.log(person2.friends); //"Shelby,Count"
动态原型模式
function Person(name,age){
this.name = name;
this.age = age;
// 方法
if(typeof this.sayName != "function"){
Person.prototype.sayName = function(){
return this.name;
}
}
}
var p = new Person("Nick",25);
console.log(p.name)
console.log(p.age)
console.log(p.sayName())
稳妥构造函数模式
function Add(a, b) {
var o = new Object();
o.say = function() {
return a;
};
return o;
}
var sum = Add(1,2);
console.log(sum.say())
ECMAScript 中的函数是对象,因此每定义一个函数,也就是实例化了一个对象
(prototype: 显式原型 ,proto : 隐式原型, 原型链:也叫隐式原型链)
还有就是要知道一点,js中的原型,原型链,上下文,是代码在宿主环境中运行时才确定的,而作用域,是在你写代码时就确定的
理解原型对象
- 每当创建 一个新函数,就会根据规则为该函数创建一个 prototype 属性,指向函数的原型对象 (即函数一旦创建,先在内部执行了一步 this.proterty = { })
- 默认情况下,所有原型对象都会自动获得一个 constructor (构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针 (函数的 原型的 构造函数 === 实例对象的 构造函数)
- 通过这个 prototype 可以在原型上添加方法和属性
- 创建自定义的函数后, 它的原型对象 默认只有一个 constructor 属性 ,而其它的方法则是从 Object 继承来的
- 创建实例之后,该实例的 内部 将包含一个指针(proto:隐式原型),指向 构造函数的 原型对象 (prototype:显式原型) (也就是说,一旦你实例化创建一个对象,会先添加一个内部的 proto
属性,这是每个对象都有的)