Giter VIP home page Giter VIP logo

blog's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

blog's Issues

shell编程入门


什么是Shell脚本

示例

看个例子吧:

#!/bin/sh
cd ~
mkdir shell_tut
cd shell_tut

for ((i=0; i<10; i++)); do
    touch test_$i.txt
done

示例解释

  • 第1行:指定脚本解释器,这里是用/bin/sh做解释器的
  • 第2行:切换到当前用户的home目录
  • 第3行:创建一个目录shell_tut
  • 第4行:切换到shell_tut目录
  • 第5行:循环条件,一共循环10次
  • 第6行:创建一个test_1…10.txt文件
  • 第7行:循环体结束

cd, mkdir, touch都是系统自带的程序,一般在/bin或者/usr/bin目录下。for, do, done是sh脚本语言的关键字。

shell和shell脚本的概念

shell是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。Ken Thompson的sh是第一种Unix Shell,Windows Explorer是一个典型的图形界面Shell。

shell脚本(shell script),是一种为shell编写的脚本程序。业界所说的shell通常都是指shell脚本,但读者朋友要知道,shell和shell script是两个不同的概念。由于习惯的原因,简洁起见,本文出现的“shell编程”都是指shell脚本编程,不是指开发shell自身(如Windows Explorer扩展开发)。

环境

shell编程跟java、php编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。

OS

当前主流的操作系统都支持shell编程,本文档所述的shell编程是指Linux下的shell,讲的基本都是POSIX标准下的功能,所以,也适用于Unix及BSD(如Mac OS)。

Linux

Linux默认安装就带了shell解释器。

Mac OS

Mac OS不仅带了sh、bash这两个最基础的解释器,还内置了ksh、csh、zsh等不常用的解释器。

Windows上的模拟器

windows出厂时没有内置shell解释器,需要自行安装,为了同时能用grep, awk, curl等工具,最好装一个cygwin或者mingw来模拟linux环境。

脚本解释器

sh

即Bourne shell,POSIX(Portable Operating System Interface)标准的shell解释器,它的二进制文件路径通常是/bin/sh,由Bell Labs开发。

本文讲的是sh,如果你使用其它语言用作shell编程,请自行参考相应语言的文档。

bash

Bash是Bourne shell的替代品,属GNU Project,二进制文件路径通常是/bin/bash。业界通常混用bash、sh、和shell,比如你会经常在招聘运维工程师的文案中见到:熟悉Linux Bash编程,精通Shell编程。

在CentOS里,/bin/sh是一个指向/bin/bash的符号链接:

[root@centosraw ~]# ls -l /bin/*sh
-rwxr-xr-x. 1 root root 903272 Feb 22 05:09 /bin/bash
-rwxr-xr-x. 1 root root 106216 Oct 17  2012 /bin/dash
lrwxrwxrwx. 1 root root      4 Mar 22 10:22 /bin/sh -> bash

但在Mac OS上不是,/bin/sh和/bin/bash是两个不同的文件,尽管它们的大小只相差100字节左右:

iMac:~ wuxiao$ ls -l /bin/*sh
-r-xr-xr-x  1 root  wheel  1371648  6 Nov 16:52 /bin/bash
-rwxr-xr-x  2 root  wheel   772992  6 Nov 16:52 /bin/csh
-r-xr-xr-x  1 root  wheel  2180736  6 Nov 16:52 /bin/ksh
-r-xr-xr-x  1 root  wheel  1371712  6 Nov 16:52 /bin/sh
-rwxr-xr-x  2 root  wheel   772992  6 Nov 16:52 /bin/tcsh
-rwxr-xr-x  1 root  wheel  1103984  6 Nov 16:52 /bin/zsh

高级编程语言

理论上讲,只要一门语言提供了解释器(而不仅是编译器),这门语言就可以胜任脚本编程,常见的解释型语言都是可以用作脚本编程的,如:Perl、Tcl、Python、PHP、Ruby。Perl是最老牌的脚本编程语言了,Python这些年也成了一些linux发行版的预置解释器。

编译型语言,只要有解释器,也可以用作脚本编程,如C shell是内置的(/bin/csh),Java有第三方解释器Jshell,Ada有收费的解释器AdaScript。

如下是一个PHP Shell Script示例(假设文件名叫test.php):

#!/usr/bin/php
<?php
for ($i=0; $i < 10; $i++)
        echo $i . "\n";

执行:

/usr/bin/php test.php

或者:

chmod +x test.php
./test.php

如何选择shell编程语言

熟悉 vs 陌生

如果你已经掌握了一门编程语言(如PHP、Python、Java、JavaScript),建议你就直接使用这门语言编写脚本程序,虽然某些地方会有点啰嗦,但你能利用在这门语言领域里的经验(单元测试、单步调试、IDE、第三方类库)。

新增的学习成本很小,只要学会怎么使用shell解释器(Jshell、AdaScript)就可以了。

简单 vs 高级

如果你觉得自己熟悉的语言(如Java、C)写shell脚本实在太啰嗦,你只是想做一些备份文件、安装软件、下载数据之类的事情,学着使用sh,bash会是一个好主意。

shell只定义了一个非常简单的编程语言,所以,如果你的脚本程序复杂度较高,或者要操作的数据结构比较复杂,那么还是应该使用Python、Perl这样的脚本语言,或者是你本来就已经很擅长的高级语言。因为sh和bash在这方面很弱,比如说:

  • 它的函数只能返回字串,无法返回数组
  • 它不支持面向对象,你无法实现一些优雅的设计模式
  • 它是解释型的,一边解释一边执行,连PHP那种预编译都不是,如果你的脚本包含错误(例如调用了不存在的函数),只要没执行到这一行,就不会报错

环境兼容性

如果你的脚本是提供给别的用户使用,使用sh或者bash,你的脚本将具有最好的环境兼容性,perl很早就是linux标配了,python这些年也成了一些linux发行版的标配,至于mac os,它默认安装了perl、python、ruby、php、java等主流编程语言。

第一个shell脚本

编写

打开文本编辑器,新建一个文件,扩展名为sh(sh代表shell),扩展名并不影响脚本执行,见名知意就好,如果你用php写shell 脚本,扩展名就用php好了。

输入一些代码,第一行一般是这样:

#!/bin/bash
#!/usr/bin/php

“#!”是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行。

运行

运行Shell脚本有两种方法:

作为可执行程序

chmod +x test.sh
./test.sh

注意,一定要写成./test.sh,而不是test.sh,运行其它二进制的程序也一样,直接写test.sh,linux系统会去PATH里寻找有没有叫test.sh的,而只有/bin, /sbin, /usr/bin,/usr/sbin等在PATH里,你的当前目录通常不在PATH里,所以写成test.sh是会找不到命令的,要用./test.sh告诉系统说,就在当前目录找。

通过这种方式运行bash脚本,第一行一定要写对,好让系统查找到正确的解释器。

这里的"系统",其实就是shell这个应用程序(想象一下Windows Explorer),但我故意写成系统,是方便理解,既然这个系统就是指shell,那么一个使用/bin/sh作为解释器的脚本是不是可以省去第一行呢?是的。

作为解释器参数

这种运行方式是,直接运行解释器,其参数就是shell脚本的文件名,如:

/bin/sh test.sh
/bin/php test.php

这种方式运行的脚本,不需要在第一行指定解释器信息,写了也没用。

变量

定义变量

定义变量时,变量名不加美元符号($),如:

your_name="qinjx"

注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样。

除了显式地直接赋值,还可以用语句给变量赋值,如:

for file in `ls /etc`

使用变量

使用一个定义过的变量,只要在变量名前面加美元符号即可,如:

your_name="qinjx"
echo $your_name
echo ${your_name}

变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:

for skill in Ada Coffe Action Java do
    echo "I am good at ${skill}Script"
done

如果不给skill变量加花括号,写成echo "I am good at $skillScript",解释器就会把$skillScript当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。

推荐给所有变量加上花括号,这是个好的编程习惯。IntelliJ IDEA编写shell script时,IDE就会提示加花括号。

重定义变量

已定义的变量,可以被重新定义,如:

your_name="qinjx"
echo $your_name

your_name="alibaba"
echo $your_name

这样写是合法的,但注意,第二次赋值的时候不能写$your_name="alibaba",使用变量的时候才加美元符。

注释

以“#”开头的行就是注释,会被解释器忽略。

多行注释

sh里没有多行注释,只能每一行加一个#号。就像这样:

#--------------------------------------------
# 这是一个自动打ipa的脚本,基于webfrogs的ipa-build书写:https://github.com/webfrogs/xcode_shell/blob/master/ipa-build

# 功能:自动为etao ios app打包,产出物为14个渠道的ipa包
# 特色:全自动打包,不需要输入任何参数
#--------------------------------------------

##### 用户配置区 开始 #####
#
#
# 项目根目录,推荐将此脚本放在项目的根目录,这里就不用改了
# 应用名,确保和Xcode里Product下的target_name.app名字一致
#
##### 用户配置区 结束  #####

如果在开发过程中,遇到大段的代码需要临时注释起来,过一会儿又取消注释,怎么办呢?每一行加个#符号太费力了,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。

字符串

字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了,哈哈),字符串可以用单引号,也可以用双引号,也可以不用引号。单双引号的区别跟PHP类似。

单引号

str='this is a string'

单引号字符串的限制:

  • 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的
  • 单引号字串中不能出现单引号(对单引号使用转义符后也不行)

双引号

your_name='qinjx'
str="Hello, I know your are \"$your_name\"! \n"
  • 双引号里可以有变量
  • 双引号里可以出现转义字符

字符串操作

拼接字符串

your_name="qinjx"
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"

echo $greeting $greeting_1

获取字符串长度:

string="abcd"
echo ${#string} #输出:4

提取子字符串

string="alibaba is a great company"
echo ${string:1:4} #输出:liba

查找子字符串

string="alibaba is a great company"
echo `expr index "$string" is`#输出:8,这个语句的意思是:找出单词is在这名话中的位置

更多

参见本文档末尾的参考资料中Advanced Bash-Scripting Guid Chapter 10.1

数组

管道

条件判断

流程控制

和Java、PHP等语言不一样,sh的流程控制不可为空,如:

<?php
if (isset($_GET["q"])) {
    search(q);
}
else {
    //do nothing
}

在sh/bash里可不能这么写,如果else分支没有语句执行,就不要写这个else。

还要注意,sh里的if [ $foo -eq 0 ],这个方括号跟Java/PHP里if后面的圆括号大不相同,它是一个可执行程序(和cd, ls, grep一样),相不到吧?在CentOS上,它在/usr/bin目录下:

ll /usr/bin/[
-rwxr-xr-x. 1 root root 33408 6月  22 2012 /usr/bin/[

正因为方括号在这里是一个可执行程序,方括号后面必须加空格,不能写成if [$foo -eq 0]

if else

if

if condition
then
    command1 
    command2
    ...
    commandN 
fi

写成一行(适用于终端命令提示符):

if `ps -ef | grep ssh`;  then echo hello; fi

末尾的fi就是if倒过来拼写,后面还会遇到类似的

if else

if condition
then
    command1 
    command2
    ...
    commandN
else
    command
fi

if else-if else

if condition1
then
    command1
elif condition2
    command2
else
    commandN
fi

for while

for

在开篇的示例里演示过了:

for var in item1 item2 ... itemN
do
    command1
    command2
    ...
    commandN
done

写成一行:

for var in item1 item2 ... itemN; do command1; command2… done;

C风格的for

for (( EXP1; EXP2; EXP3 ))
do
    command1
    command2
    command3
done

while

while condition
do
    command
done

无限循环

while :
do
    command
done

或者

while true
do
    command
done

或者

for (( ; ; ))

until

until condition
do
    command
done

case

case "${opt}" in
    "Install-Puppet-Server" )
        install_master $1
        exit
    ;;

    "Install-Puppet-Client" )
        install_client $1
        exit
    ;;

    "Config-Puppet-Server" )
        config_puppet_master
        exit
    ;;

    "Config-Puppet-Client" )
        config_puppet_client
        exit
    ;;

    "Exit" )
        exit
    ;;

    * ) echo "Bad option, please choose again"
esac

case的语法和C family语言差别很大,它需要一个esac(就是case反过来)作为结束标记,每个case分支用右圆括号,用两个分号表示break

函数

定义

调用

文件包含

可以使用source和.关键字,如:

source ./function.sh
. ./function.sh

在bash里,source和.是等效的,他们都是读入function.sh的内容并执行其内容(类似PHP里的include),为了更好的可移植性,推荐使用第二种写法。

包含一个文件和在执行一个文件一样,也要写这个文件的路径,不能光写文件名,比如上述例子中:

. ./function.sh

不可以写作:

. function.sh

如果function.sh是用户传入的参数,如何获得它的绝对路径呢?方法是:

real_path=`readlink -f $1`#$1是用户输入的参数,如function.sh
. $real_path

用户输入

执行脚本时传入

脚本运行中输入

select菜单

stdin和stdout

常用的命令

sh脚本结合系统命令便有了强大的威力,在字符处理领域,有grep、awk、sed三剑客,grep负责找出特定的行,awk能将行拆分成多个字段,sed则可以实现更新插入删除等写操作。

ps

查看进程列表

grep

排除grep自身

查找与target相邻的结果

awk

sed

插入

替换

删除

xargs

curl

综合案例

参考资料

关于坚持,关于成功学

我觉得自己最近越来越懒散了,可能是因为「winter is comming」。

借了一本书,准备这周内看完,现在才刚看了个序。
本来准备20号把 mustache 源码读完,顺便写篇博客。但截止到今天,刚80%,博客还没影。
话说最近在做测试,发现了测试时候的一些痛点,想解决掉。可是确迟迟没能下手。

好多好多的事情,其实到了现在,竟然一个还没成。现在只能假装很忙的样子在公司加班,还忙里偷闲,想写点儿东西。又一次陷入了拖延症的怪圈。谁让我一不小心吃了公司的加班餐呢!自作孽,不可活。

下面咱们来说说成功学吧。我记得很小的时候我就很瘦,很弱小,还经常生病。所以我就特别希望能成为一个肌肉男,最起码也得是个死胖子。而我堂哥,那时候可是标准的肌肉男,他可以单手把我拎起来,就像拎小鸡一样。这让我一度很苦恼,于是我就发奋要成为一个像他那么健壮的男子。那时候,他经常锻炼,每天要举一个很重的杠铃。于是,我也举,每天都举。就这样居然也坚持了两个多月。但我还是很瘦,还是很弱小,经常生病。

那时候老师常教育我们凡事要坚持,只要坚持就能成功,只能努力就一定有收获。可是我还是那个我,身体并没有变强壮。也许你会说,肯定是我坚持的还不够久。好吧,我只想说,郭敬明就算再努力去增高,也到不了姚明的胳肢窝。这就是差别,不是努力所能弥补的。有些事情,明知山有虎,就别再**似的还向虎山行了,你又不是武松。

很多事情,不是努力就能有结果的。所以最重要的就是了解自己,了解你所做的事。是否值得,生命只有一次,没有必要去浪费太多的经历去做一件你就算很努力去做,也可能没别人随便做做好的事情上。 认清自己,知其不可为而为之,那是勇气,也是愚蠢。你的明知徒劳的努力,可能只是为了让自己觉得不后悔而已,但是那又能有什么卵用。这和阿Q的那套理论又有什么差别。「虽然失败了,但至少我曾经为它努力奋斗过」,不过是自我感动而已。永远不要跟天才比天赋,也不要跟**比下限。

看到过一句话,我觉得竟很有道理。「这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。」———By于宙.《我们这一代人的困惑》

深入理解 javascript 原型

深入理解javascript原型继承

作为一门被长期误解的编程语言,javascript一直被人所诟病.但是如果你真正的了解它之后,你会深深的爱上它.

首先,javascript是一个面向对象的编程语言,而且是一个纯粹的面向对象.虽然很多人不能理解,因为在他们眼中,只有像java,c++这样的编程语言才能称之为面向对象.但是,我只想说,你误解我,是因为你不懂我.

JavaScrip秘密花园在JavaScrip中,一切变量皆对象,除了两个特殊值undefined 和 null.

什么是面向对象?

  • 一切事物皆对象
  • 对象具有封装和继承特性

- 对象与对象之间使用消息通信,各自存在信息隐藏

以这三点做为依据,C++ 是半面向对象半面向过程语言,因为,虽然他实现了类的封装、继承和多态,但存在非对象性质的全局函数和变量。Java、C# 是完全的面向对象语言,它们通过类的形式组织函数和变量,使之不能脱离对象存在。但这里函数本身是一个过程,只是依附在某个类上。

然而,面向对象仅仅是一个概念或者编程**而已,它不应该依赖于某个语言存在。比如 Java 采用面向对象**构造其语言,它实现了类、继承、派生、多态、接口等机制。但是这些机制,只是实现面向对象编程的一种手段,而非必须。换言之,一门语言可以根据其自身特性选择合适的方式来实现面向对象。所以,由于大多数程序员首先学习或者使用的是类似 Java、C++ 等高级编译型语言(Java 虽然是半编译半解释,但一般做为编译型来讲解),因而先入为主地接受了“类”这个面向对象实现方式,从而在学习脚本语言的时候,习惯性地用类式面向对象语言中的概念来判断该语言是否是面向对象语言,或者是否具备面向对象特性。这也是阻碍程序员深入学习并掌握 JavaScript 的重要原因之一。

实际上,JavaScript语言是通过一种叫做原型(prototype)的方式来实现面向对象编程的。它和其他的面向对象类编程语言一样,只是它的实现方式不同而已,或者说他们采用了不同的面向对象设计哲学。

在基于类的面向对象方式中,对象(object)依靠类(class)来产生。而在基于原型的面向对象方式中,对象(object)则是依靠 构造器(constructor)利用 原型(prototype)构造出来的。

举个客观世界的例子来说明二种方式认知的差异。例如工厂造一辆车,一方面,工人必须参照一张工程图纸,设计规定这辆车应该如何制造。这里的工程图纸就好比是语言中的 类 (class),而车就是按照这个 类(class)制造出来的;另一方面,工人和机器 ( 相当于 constructor)利用各种零部件如发动机,轮胎,方向盘 ( 相当于 prototype 的各个属性 ) 将汽车构造出来。

在这里我不想讨论太多这两种面向对象究竟孰优孰劣的问题,这个讨论来讨论去也不会有定论.但是以为个人观点我更倾向于基于原型的面向对象.因为一切事物皆对象.现实世界中的所有对象也都是由其他对象构造出来的,而紧紧依靠图纸是没法产生一个现实中的汽车的.这个设计更符合现实世界的客观规律.

JavaScript的面向对象来源于‘self’这个牛逼但短命的编程语言。

Self语言把概念上的精简作为设计原则。它取消了类的概念,只有对象的概念,同时把消息作为最基本的操作。把对象的属性理解为获取或更改属性这两种方法,从而把属性的概念简化为方法;取消了变量和赋值,并以通过消息来读槽和写槽的方式代之。

在 JavaScript 中,prototype 是函数的一个属性,同时也是由构造函数创建的对象的一个属性。 函数的原型为对象。 它主要在函数用作构造函数时使用。

在编程语言中,目前存在两种继承方式:类继承和原型继承.

  • 类继承:

- 原型继承

而由于java等主流编程语言的支持使得类继承被大众所普遍接受

那么,JavaScript中是如何实现的基于原型的面向对象?

要理解原型继承,首先得先熟悉几个概念,咱们一步步说起:

如何生成对象?

-1.声明对象直接量:JSON

    var obj = {
        name: "jack",
        eat: "bread"
    }
    console.log(typeof obj);

-2.使用构造函数生成一个新的对象

//构造函数
var Foo = function(name){
    this.name = name;  //私有属性    
}

//原型方法和属性,被继承时候才会调用
Foo.prototype.run = function(){
    alert("I'm running so fast that can't stop at all!");
}

var kick = new Foo("kick");
console.log(typeof kick);
console.log(kick.name);
kick.run();

-3.使用使用Object.create创建对象
ECMAScript 5中引入了一个新方法: Object.create. 可以调用这个方法来创建一个新对象. 新对象的原型就是调用create方法时传入的第一个参数:

先来看一下create方法是如何实现的,该方法来源于Douglas Crockford,现在已被ECMAScript 5引入:

Object.create = function (parent) {
    function F() {}
    F.prototype = parent;
    return new F();
};

这个看起来很简洁,而且能够完全代替new的用法,毕竟new关键字并不真正的属于JavaScrip的原型模式.它先是声明了一个构造器,然后将其原型设置为你想要的值,最后返回生成的新对象.其实就是封装了new.

下面这段代码就是真正的原型继承了.look:

var Point = {
    x: 0,
    y: 0,
    print: function () { console.log(this.x, this.y); }
};
var p = Object.create(Point);  //new一个对象
p.x = 10;
p.y = 20;
p.print(); // 10 20

code:

function Plant(name,year){
    this.name = name;
    this.year = year || 0;
}
var tree.prototype = new Plant('tree');
tree.prototype.grow = function(){
    this.year ++;
}
tree.prototype.old = functiono(){
    console.log(this.year);
}

上面这段代码使用原型实现了一个简单的对象继承.下面来分析下上面这段代码
首先是声明了一个构造函数,构造函数和普通函数有什么区别?构造函数可以使用new调用,生成一个新的对象.
如果想要在对象上添加方法,可以将方法写在对象的原型上.
子类继承父类,只需要把父对象复制给自对象的原型上即可.

JavaScrip原型链(prototype chain)

下面这段是ECMAScript关于原型的解释

ECMAScript does not contain proper classes such as those in C++, Smalltalk, or Java, but rather, supports constructors which create objects by executing code that allocates storage for the objects and initialises all or part of them by assigning initial values to their properties. All constructors are objects, but not all objects are constructors. Each constructor has a Prototype property that is used to implement prototype-based inheritance and shared properties. Objects are created by using constructors in new expressions; for example, new String("A String") creates a new String object. Invoking a constructor without using new has consequences that depend on the constructor. For example, String("A String") produces a primitive string, not an object.

ECMAScript supports prototype-based inheritance. Every constructor has an associated prototype, and every object created by that constructor has an implicit reference to the prototype (called the object's prototype) associated with its constructor. Furthermore, a prototype may have a non-null implicit reference to its prototype, and so on; this is called the prototype chain. When a reference is made to a property in an object, that reference is to the property of that name in the first object in the prototype chain that contains a property of that name. In other words, first the object mentioned directly is examined for such a property; if that object contains the named property, that is the property to which the reference refers; if that object does not contain the named property, the prototype for that object is examined next; and so on.

依据我的理解就是说:
JavaScrip可以采用构造器(constructor)生成一个新的对象,每个构造器都拥有一个prototype属性,而每个通过此构造器生成的对象都有一个指向该构造器原型(prototype)的内部私有的链接(proto),而这个prototype因为是个对象,它也拥有自己的原型,这么一级一级指导原型为null,这就构成了原型链.

这里我们涉及到了一个隐匿属性proto**,那么proto和prototype究竟有什么区别嘞?**
注: proto 是一个不应在你代码中出现的非正规的用法,这里仅仅用它来解释JavaScript原型继承的工作原理。

知道了JavaScrip原型链的存在之后,让我们来看下它的实现,下面这段代码展示了原型链是如何工作的.

function getProperty(obj, prop) {
    if (obj.hasOwnProperty(prop)) //首先查找自身属性,如果有则直接返回
        return obj[prop]
    else if (obj.__proto__ !== null)
        return getProperty(obj.__proto__, prop) //如何不是私有属性,就在原型链上一步步向上查找,直到找到,如果找不到就返回undefind
    else
        return undefined
}

So,如果proto可以使用的话,我们可以通过下面这种方式实现继承:

    var person = {
        city: "Beijing",
        hate: function(){
            alert("I really hate the PM2.5 and the foggy wether!");
        }
    }
    var lee = {
        name: "lee",
        age: "18",
        __proto__: person
    }
    console.log(lee);
    lee.hate();

这都什么玩意儿,不是要用new吗.事实上,事情不是这么简单滴, 为了和主流的类继承扯上那么一点儿关系,JavaScrip引入了'new'关键字,引入了构造函数.所以通常我们看到的是下面这样的:

    var Person = function(name,age){
        this.name = name;
        this.age  = age;
    };
    Person.prototype = {
        city: "Beijing",
        hate: function(){
            alert("I really hate the PM2.5 and the foggy wether!");
        }
    }
    var lee = new Person('lee',18);
    console.log(lee.name);
    lee.hate();

我们需要一个像类一样的东西,于是有了构造函数,我们得有一个通过类生成实例的过程,于是又出现了new.这么一来JavaScrip的原型继承似乎就变得不伦不类了.虽然JavaScrip的原型继承来源于'self',但是却追随了类继承的形式.罪过,不过话说回来,也许就是因为这种妥协才让JavaScrip能够流行起来,并成为了现在最流行的原型继承语言,而self,说实话,它独特写法确实挺难让人接受的.

    var Foo = function(){
        this.name = "foo";
    }
    Foo.prototype.say = function(){
        alert("Hello World!");
    }
    var foo = new Foo();
    console.log(foo.__proto__); //私有链接,指向构造函数的原型
    console.log(Foo.prototype);  
    console.log(foo.__proto__ === Foo.prototype); //true
    console.log(foo.__proto__.constructor === Foo); //true



 // 声明 Animal 对象构造器
 function Animal(name) { 
    this.name = name;
 } 
 // 将 Animal 的 prototype 属性指向一个对象,
 // 亦可直接理解为指定 Animal 对象的原型
 Animal.prototype = {
    weight: 0, 
    eat: function() { 
        alert( "Animal is eating!" ); 
    } 
 }
 // 声明 Mammal 对象构造器
 function Mammal() { 
    this.name = "mammal"; 
 } 
 // 指定 Mammal 对象的原型为一个 Animal 对象。
 // 实际上此处便是在创建 Mammal 对象和 Animal 对象之间的原型链
 Mammal.prototype = new Animal("animal"); 
 // 声明 Horse 对象构造器
 function Horse( height, weight ) { 
    this.name = "horse"; 
    this.height = height; 
    this.weight = weight; 
 }
 // 将 Horse 对象的原型指定为一个 Mamal 对象,继续构建 Horse 与 Mammal 之间的原型链
 Horse.prototype = new Mammal(); 
 // 重新指定 eat 方法 , 此方法将覆盖从 Animal 原型继承过来的 eat 方法
 Horse.prototype.eat = function() { 
    alert( "Horse is eating grass!" ); 
 }
 // 验证并理解原型链
 var horse = new Horse( 100, 300 ); 
 console.log( horse.__proto__ === Horse.prototype ); 
 console.log( Horse.prototype.__proto__ === Mammal.prototype ); 
 console.log( Mammal.prototype.__proto__ === Animal.prototype ); 
 //原型链
 Horse-->Mammal的实例
 Mammal-->Animal的实例
 Animal -->Object.prototype

在 ECMAScript 中,每个由构造器创建的对象拥有一个指向构造器 prototype 属性值的 隐式引用(implicit reference),这个引用称之为 原型(prototype)。进一步,每个原型可以拥有指向自己原型的 隐式引用(即该原型的原型),如此下去,这就是所谓的 原型链(prototype chain) 参考资源。在具体的语言实现中,每个对象都有一个 proto 属性来实现对原型的 隐式引用。

我们已经了解了JS原型继承是什么,以及JS如何用特定的方式来实现之。然而使用真正的原型继承(如 Object.create 以及 proto)还是存在以下缺点:

  • 标准性差:proto 不是一个标准用法,甚至是一个不赞成使用的用法。同时原生态的 Object.create 和道爷写的原版也不尽相同。
  • 优化性差: 不论是原生的还是自定义的 Object.create ,其性能都远没有 new 的优化程度高,前者要比后者慢高达10倍。

到了这里我们基本对JavaScrip的原型继承有了一个更深层的认识了.通过历史回溯我们也了解了为什么JavaScrip会变成现在这个不伦不类的样子.
JavaScrip是一个完全的面向对象函数式编程语言,采用原型继承,虽然写法类似类继承.但是我们不能因此就认为它不是面向对象的编程语言.而且nodejs的出现,又让JavaScrip在编程语言界火了一把.所以是时候拥抱JavaScrip了.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.