本科时被一个笑话笑的肚子疼,问哪一句话会立即在互联网论坛里掀起腥风血雨。
答:PHP 是全世界最好用的语言
怎么会是 PHP 呢,一定是 Javascript,即可前端,也可服务器。
玩笑归玩笑。我个人最喜欢的语言是 Javascript,不是我真觉得它有多好,而是我最熟悉的就是它了,爱一行才能干一行嘛。因为最近用 Framer 做前端动效,发现制作难的效果需要更深层的理解面向对象编程。而 Framer 的代码是 Coffeescript, Coffeescript 又是由 Javascript 改编而来的。所以决定啃下 JS 面向对象编程这块硬骨头,向自己从前的面向过程编程说 Bye Bye.
正题,我是看 Udacity 的视频教程学习的,文末有视频链接,部分内容包涵个人理解,理解有误之处还请指正。
Scope
JS 的作用域有两种,第一种是全局作用域 Global,第二种是函数作用域 Function. 全局作用域在代码执行过程中一直存在,函数作用域在函数执行完之后消亡。
二者在层级关系上有着嵌套的关系,Global 嵌套着 Function,当代码执行时,从最底层 Scope 逐层往上搜寻变量。
Closures
闭包,我的理解是,在 Function 执行后,依然能够调用 Function 中的变量。即局部作用域的内容再执行后,没有被销毁,依然可以被调用。
有 2 种方式可以实现闭包:
1.return
function Car(){
var price = 3000;
return function(){
return price;
};
}
var obj = Car();
# Car 函数中的 price 仍然被调用了,没有被销毁
console.log(obj());
2.将其赋值给全局变量
var obj =[];
function Car(){
var price = 3000;
obj.push(price);
}
Car();
# Car 函数中的 price 仍然被调用了,没有被销毁
console.log(obj[0]);
This
JS 的 this 变量以前一直让我很困惑,不是很清楚怎么使用。今天终于弄清楚了,个人总结的一句话是
this 指向的是代码执行时的执行环境
比如:
var method = 2;
var obj = {
method: 1,
outputMethod: function(){
console.log(this.method);
}
}
# 输出的结果是 1
obj.outputMethod();
简单的说,this 指向的是.
左边的对象,如果.
左边为空,则指向 Global. 有一个例外,就是 new
, new
会创建一个完全新的对象,this 指向该全新的对象,并返回该对象。如下:
var Car = function(p1){
# this = Object.create();
this.p1 = p1;
# return this;
}
# this 指向 Car 类的实例 ford
var ford = new Car('1');
Prototype chains
原型链是 JS 一个很有特色的机制,一个对象可以引用其上层对象的属性和方法。这样可以让把重复的内容写在同一个 prototype 里面,节省存储空间。
var man = {gender: 'male'}
# boy 可以引用 man 的属性
var boy = Object.create(man);
# man 的上层对象是 Object, 因此还可以调用其 toString() 方法
boy.toString();
Object decorator pattern
写代码注意其可复用性,尽量写完代码后,审查一下是否可以有优化的空间,将相同的代码提取并合并,方便以后修改。
var man1 = {age:0};
man1.age++;
var man2 = {age:0}
man2.age++;
# man 作为一个有 age 的对象,可以提取出来。其次,age++ 这个操作可以提出出来单独成为一个函数。上述代码可以转成如下。
var man = function(obj, age){
var obj = obj;
obj.age = age;
obj.agePlus = function(){
obj.age++;
}
return obj;
}
var man1 = man({}, 0);
man1.agePlus();
var man2 = man({}, 0);
man2.agePlus();
代码量小的时候,可能看不出来优势。但是当代码量大的,提升的效率是几何级的了。设计师可以把这种提取看作是 Symbol 控件的整合。当你需要把页面中的 100 个相同按钮改变样式的时候,没有定义 Symbol 的你可能眼角会流下泪水吧。
Functional classes
JS 中,在我理解,所有内容本质上对象,这样一来,函数本身也是对象。因此可以把具有特性的函数进一步优化,成为具有一个特定功能的类,Class.
# 类首字母一般大写
var Man = function(age){
var obj = {age: age};
obj.agePlus = function(){
obj.age++;
}
return obj;
}
var man1 = man(0);
man1.agePlus();
var man2 = man(0);
man2.agePlus();
此时 Man 函数就成了一个类,具有 age 属性。当然你可以扩展它,让其拥有更多的属性和方法。
Prototypal classes
由于很多人使用函数来定义类,为了优化人们的操作,JS 官方给了 new
方法,用于省去人们创建对象,会返回对象的操作。
# 类首字母一般大写
var Man = function(age){
# 不需要了
# var obj = {age: age};
# this 指向的 Man 每次创建的实例 instance
this.obj = obj;
obj.agePlus = function(){
obj.age++;
}
# 不需要了
# return obj;
}
var man1 = new man(0);
man1.agePlus();
var man2 = new man(0);
man2.agePlus();
Pseudoclassical patterns
伪类模式提出了 Prototype 的概念,起用于存储公共方法,节省内存空间,也是 JS 一个特性,也便于对 JS 类的扩展。
为了减少用户的工作量,JS 定义了 prototype, 用于存储这里 Car.methods. 我的理解是,把 prototype 想成一个单独的对象,类通过 prototype 调用其方法,prototype 通过 constructor 查询其构造函数。二者没有代理关系。但是通过 Class 创建的 instance 和 prototype 是代理关系,并可以直接调用其方法。
# Function 版本
var Car = function(price){
# obj 继承了 Car.methods 的所有方法
var obj = Object.create(Car.method);
this.price = price;
return obj;
}
# 定义公共方法
var Car.methods = {
showPrice: function(){
console.log(this.price);
}
}
var ford = new Car(1);
ford.showPrice();
# 下面是 prototype 版本
var Car = function(price){
# 不用了
# var obj = Object.create(Car.method);
this.price = price;
# 不用了
# return obj;
}
# 定义公共方法
var Car.prototype.methods = {
showPrice: function(){
console.log(this.price);
}
}
var ford = new Car(1);
ford.showPrice();
Pseudoclassical subclasses
有的时候人们会想定义几个具有功能属性的类,但是又不想重复类之间的属性。这时便有了父类,子类,继承的编码思想出现了。好比,喜羊羊和灰太狼都是动物,它们有公共的属性,可以设置在父类里,还可以添加一个共有的显示价格方法, 但是它们也有各自独特的属性。
# 定义父类
var Animal = function(price){
this.type = 'animal';
this.price = price;
}
# 定义父类方法
Animal.prototype.showPrice = function(){
console.log(this.price);
}
#定义子类
# 继承父类的属性, 同时继承父类的属性
var Chicken = function(price, wings){
# call()函数将 Animal.price 的执行环境改成 this, 由于 this 指向 Chicken 的实例,
# 因此 Chicken 实例就继承了 Animal 的属性
Animal.call(this, price);
# 定义子类属性,鸡是有翅膀的
wings = true;
}
# 继承父类的方法
# 把 Chicken.prototype 指向 Animal.prototype
# 从而 Chicken 的实例可以调用 Animal.prototype 的方法,真的膜拜 JS 发明人
Chicken.prototype = Object.create(Animal.prototype);
var chicken1 = new Chicken(10)
chicken1.showPrice();
总结
明确这些概念,以及这些概念背后的原因,对我下一步的学习有着很大的裨益。以前我觉得我看不懂的东西,耐心的思考一下,是可以看懂的。世上无难事,只怕有心人嘛。
下一步打算把教程后面的游戏 Project 给完成,用实战再次强化自己编程能力,最后用 Framer 把游戏再转到 Framer 平台上。自己画图,把游戏界面再做漂亮点。
希望一起练习的同学请和我联系,共同进步。