Web 阮神js学习笔记_面向对象
对象
js 没有类,只有对象,万物皆对象
数据和行为封装在一起,就是对象.
创建
- 直接创建
var obj = {
name:"yy"
age:1
introduce: function(){ };
}
- 构造函数方式
var Obj = funciton(){ //1字母大写,非强制
this.name = "yy"; //2含this
this.age = 1;
this.introduce= function(){};
// no return,如果返回的是对象,则返回new出来的也是返回的对象
}
var obj = new Obj(); //3 new出来
使用 new 命令时,它后面的函数依次执行下面的步骤。
1 创建一个空对象,作为将要返回的对象实例。
2 将这个空对象的原型,指向构造函数的prototype属性。
3 将这个空对象赋值给函数内部的this关键字。(apply)
4 开始执行构造函数内部的代码。
this
this 就是属性或方法“当前”所在的对象,当前意味着 this 可变.
到底怎么来的?
对象是内存里的k-v组成
实际上k又是包含在属性描述对象里,所以对象本质上是由很多的属性描述对象构成.
当属性的值是函数时,实际就是c里的1个函数地址.
函数在不同的环境(栈)下,可理解为传入的参数可以不同,有不同的表现!
this它在js里的设计目的就是在函数体内部,指代函数当前的运行环境.
这个环境也是个对象,即当前方法所在的对象.
带 this 的就是构造函数?No,也许是希望 f 被不同的对象引用时,this 指向不同的对象:
function f() {
return "姓名:" + this.name;
}
this 环境总结:
1 全局环境使用this,它指的就是顶层对象window。
2 构造函数中的this,指的是实例对象。
3 如果对象的方法里面包含this,this的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变this的指向。
方法运行时所在的对象是重点,划出来要考...
情况 3 的例子
var A = {
name: "张三",
describe: function() {
return "姓名:" + this.name;
}
};
A.descire(); //'张三'
var name = "李四";
var f = A.describe; //f在顶层,因此describe里的this也指向顶层,
f()(
// this.name = '李四'.
A.describe
)(); //同理,'李四'
var A = {
p: "张三",
m: {
info: function() {
console.log(this.p);
},
p: "李四"
}
};
A.m.info(); // ‘李四’,info所在的运行时对象为A.m
再来个例子:
var o = {
f1: function () {
console.log(this);
var f2 = function () {
console.log(this); //本this运行时所在的对象为f2,为1个临时对象,临时对象
//都可以理解为在顶层创建的?
}();
}
}
o.f1() // Object Window
//等效于
var temp = function(){console.log(this);}
var o = {
f1:unction(){
console.log(this);
var f2 = temp();
} //不变
}
解决办法:保存第 2 层的 temp this,用的非常多
var o = {
f1: function() {
console.log(this);
var that = this;
var f2 = (function() {
console.log(that);
})();
}
};
o.f1();
再来个例子:
var o = {
v: "hello",
p: ["a1", "a2"],
f: function() {
var that = this;
this.p.forEach(function(item) {
console.log(that.v + " " + item);
});
}
};
o.f(); //hello hello
一句话: 对象方法想把this传给自己的子函数,就先保存我自己变成that,后代再用that
.
绑定 this
call: func.apply(thisValue, arg1,arg2,...)
apply: func.apply(thisValue, [arg1, arg2, ...])
bind: func.bind(thisValue,arg1,arg2), 返回新的函数, 添加新的参数, 将func的运行环境设置为了thisValue.
fn.call(obj,a1,a2...)
apply 和 call 的唯一区别:apply 的参数传入可以是数组.
thisValue 就是改变调用者的 this 环境.
- slice 的例子
var a = [1, 2, 3];
a.slice(); //[1,2,3],不指定start,end,输出不变
var args = Array.prototype.slice.call(arguments); //arguments转数组. arguments
本质可以可理解为:
Array.prototype.slice = function(start, end) {
var result = new Array();
start = start || 0;
end = end || this.length; //this指向调用的对象,当用了call后,能够改变this的指向,也就是指向传进来的对象,这是关键
for (var i = start; i < end; i++) {
result.push(this[i]);
}
return result;
};
- 其他
//1
var a = [10, 2, 4, 15, 9];
Math.max.apply(null, a); // 15
//2
Array.apply(null, ["a", , "b"]); // [ 'a', undefined, 'b' ]
//3
var o = new Object();
o.f = function() {
console.log(this === o); //希望this指向o
};
$("#button").on("click", o.f.apply(o));
- bind 例子
var counter = {
count: 0,
inc: function() {
this.count++;
}
};
var func = counter.inc.bind(counter);
func();
counter.count; // 1
继承
原型对象 prototype
function 才有 prototype 属性,叫做原型对象
构造函数的 prototype 属性才有意义,被其实例化的对象,共享 prototype 的属性和方法。这也是它被称为原型对象的原因.
function Cat() {
this.age = 12;
}
Cat.prototype.color = "red";
c1 = new Cat();
c2 = new Cat();
c1.color; // red 共享了原属性
c2.color; // red
Cat.prototype.color = "blue";
c1.color; // blue
c1.color = "red"; // 设置实例对象本身的color属性,优先级最高,覆盖原原型对象的属性
c2.color; //bule
原型对象是 js 继承的基础,因为会共享所有原型对象的属性,形成链式继承.
instanceof
检查整个原型链,因此同一个实例对象,可能会对多个构造函数都返回 true。
注意,instanceof 运算符只能用于对象,不适用原始类型的值
a = new Array();
a instanceof Array; //true
a instanceof Object; //true
s = "abc";
s instanceof Array; //false 只适应于对象
construtor
constructor 是 prototype 的1个属性,指向原型对象所在的的构造函数.
function P() {}
var p = new P();
p.constructor === P; // true
p.constructor === P.prototype.constructor; // true
p.hasOwnProperty("constructor"); // p没有constructor属性,来自prototype
var pp = new p.constructor(); //通过实例,而不是构造函数来创建新对象也是可以的.
直接更新对象的方式
来修改原型对象时,一般要同时修改 constructor 属性的指向。
function Person(name) {
this.name = name;
}
Person.prototype.constructor === Person; // true
Person.prototype = {
a: function() {}
};
Person.prototype.constructor === Person; // false
Person.prototype.constructor === Object; // constructor来自普通对象
所以一般不要像上面那样更新 prototype,而是:
C.prototype.method1 = function (...) { ... };
子类继承父类的写法
// 第一步,子类继承父类的实例
function Rectangle() {
Shape.call(this); // 调用父类构造函数
}
// 另一种写法
function Rectangle() {
this.base = Shape;
this.base();
}
// 第二步,子类继承父类的原型
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
如果只是继承某个方法,(并无实际作用感觉,破坏了对象的继承原则)
ClassB.prototype.print = function() {
ClassA.prototype.print.call(this); //调用A的print方法,但是this对象使用B的.
};
Mixin 模式
js 模块化
模块就是封装,仅提供方法,不提供变量.
独立性是模块的重要特点,模块内部最好不与程序的其他部分直接交互。 为了在模块内部调用全局变量,必须显式地将其他变量输入模块
var module1 = (function($, YAHOO) {
//...
})(jQuery, YAHOO);
立即执行函数(IIFE)
立即执行函数”(Immediately-Invoked Function Expression,IIFE).
外部无法访问私有变量_count
模块化的思想,非常常见了.
var module1 = (function() {
var _count = 0;
var m1 = function() {
//...
};
var m2 = function() {
//...
};
return {
m1: m1,
m2: m2
};
})();
console.info(module1._count); //undefined
还能起到作用如命令空间
,这个倒是很常见的写法
(function($, window, document) {
function go(num) {
//go仅模块内部使用
}
//external定位在windows上,全局可使用
window.external = {
init: initialize,
destroy: dieCarouselDie
};
})(jQuery, window, document);
放大模式
是为了模块里扩充,添加其他方法,或者模块的方便:
var module1 = (function(mod) {
mod.m3 = function() {
//...
};
return mod;
})(module1);
宽放大模式
允许模块为空,
var module1 = (function(mod) {
//...mod可能来自其他部分,可能加载到空对象
return mod;
})(window.module1 || {});