js 笔记3
接笔记2
13.创建对象
- 工厂模式
function createNewObject(name, age, job) {
let o = new Object();
o.name = name;
o.age = age;
o.job = job
o.sayName = function() {
console.log(name);
}
return o;
};
let me = createNewObject('Ethan', 24, 'java');
console.log(me.name); // Ethan
- 构造函数模式
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
console.log(this.name);
}
};
let p = new Person('Ethan', 24, 'java')
console.log(p.name); // Ethan
以这种方式调用构造函数实际上会经历以下 4 个步骤:
- 创建一个新对象;
- 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
- 执行构造函数中的代码(为这个新对象添加属性);
- 返回新对象。
通过构造函数模式创建出来的 p 对象有一个 constructor 属性,该属性指向 Person。并且通过构造函数可以将实例标识为一种特定的类型,这是工厂模式做不到的。
console.log(p.constructor == Person); // true
console.log(p instanceof Person); //true
如果不使用 new 来声明构造函数:this 就会指向 global 对象。
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
console.log(this.name);
}
};
Person('zzz', 24, 'js');
global.sayName();
let o = new Object();
Person.call(o, 'yyy', 23, 'vue');
o.sayName(); // yyy
global.sayName(); // zzz
以上构造函数有个问题:使用构造函数的主要问题,就是每个方法都要在每个 实例上重新创建一遍 。
所以,做如下改进:
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
};
function sayName() {
console.log(this.name);
}
let p1 = new Person('Ethan', 24, 'java');
p1.sayName(); // Ethan
let p2 = new Person('aa', 22, 'bb');
console.log(p1.sayName == p2.sayName); // true,而用之前的方式结果为 false
14.原型模式
是什么:每个函数都有一个prototype
(原型)属性,该属性可以看成一个指针,指向一个对象,这个对象的用途是包含了特定对象的所有实例共享的属性和方法。可以把prototype
理解为通过构造函数而创建的那个对象实例的原型对象。使用原型对象的好处就是可以让所有对象实例共享它所包含的属性和方法。所以,我们不必再构造函数中定义对象实例的信息,而是将这些信息直接添加到原型对象中。
因此,之前的构造函数可以写成这样:
function Person() {
};
Person.prototype.name = 'Ethan';
Person.prototype.age = 24;
Person.prototype.job = 'java';
Person.prototype.sayName = function () {
console.log(this.name);
}
let p1 = new Person();
p1.sayName(); // Ethan
let p2 = new Person();
console.log(p1.sayName == p2.sayName); // true
理解:看图吧,构造函数、构造函数的对象、prototype
、constructor
、[[Prototype]](_PROTO_)
之间的爱恨情仇。
当我们调用一个对象的属性时,都会进行一次搜索,先搜索该对象是否有相同名称的属性,再去搜索该对象的原型是否有该属性。所以如果我们给一个对象的原型已经有的属性重新赋值,并不会影响对象的原型,只会应该该对象的属性。可以通过hasOwnProperty()
来判断该属性是实例属性还是原型属性。还有一个方法是in
,比如"name" in person1
,这个方法不管该属性是在实例属性中还是原型属性中,都会返回true
。
除此之外,还有个方法返回对象所有的课枚举属性。
function Person() {
};
Person.prototype.name = 'Ethan';
Person.prototype.age = 24;
Person.prototype.job = 'java';
Person.prototype.sayName = function () {
console.log(this.name);
};
let keys = Object.keys(Person.prototype);
console.log(keys); // [ 'name', 'age', 'job', 'sayName' ]
当然,上面的原型语法比较复杂,可以写成下面这种相对简单的:
function Person() {
};
Person.prototype = {
// constructor: Person, // 最后一段话也可以用这行代替
name: 'Ethan',
age: 24,
job: 'java',
sayName: function() {
console.log(this.name);
}
};
// 因为每创建一 个函数,就会同时创建它的 prototype 对象,这个对象也会自动获得 constructor 属性,所以加上这个
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
value: Person
});
原型模式的缺点:原型中所有属性是被很多实例共享的,这种共享对于函数非常合适。 说简单点就是修改一个对象的属性会影响到另一个对象的该属性。
15.组合使用构造函数模式和原型模式
创建自定义类型最常用的方法,构造函数模式用来定义实例属性,原型模式用来定义方法和共享的属性。这样,每个实例都有自己的一份实例属性的副本,但同时又共享着对方法的引用,节省了内存。还可以向构造函数传递参数。
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
Person.prototype = {
constructor: Person,
sayName: function () {
console.log(this.name);
}
}
let p1 = new Person('aa', 10, 'java');
let p2 = new Person('bb', 20, 'js');
p1.sayName(); // aa
p2.sayName(); // bb
p1.name = 'cc';
p1.sayName(); // cc
p2.sayName(); // bb
16.js 中的继承
就说两种吧:
- 组合继承
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
//继承属性 SuperType.call(this, name);
this.age = age;
}
//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors);
instance1.sayName();
instance1.sayAge();
//"red,blue,green,black"
//"Nicholas";
//29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors);
instance2.sayName();
instance2.sayAge();
//"red,blue,green"
//"Greg";
//27
- 更完善的:寄生组合继承
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
SuperType.call(this, name);
this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
};
17.闭包
关于闭包,直接看这篇吧,我觉得说的挺易懂的。
18. BOM 中常用 api
window.innerHeigt
window.innerWidth
window.outerHeigt
window.outerWidth
window.location
=== document.location
location.assign
// 下面三行代码效果完全相同
window.location = "http://www.google.com";
location.href = "http://www.google.com";
location.assign = "http://www.google.com";
Navigator
:获取浏览器的信息找这个。
screen
:屏幕的信息找这个。
history.go(-1)
history.go(1)
history.back()
history.forward()