***ruby的环境建置简要(也适合重装第二台mac)

前提:

确认 Mac 操作系统为 10.10 ( Yosemite ) 以上

一、安装 Xcode
程序开发环境的辅助应用程序
在app store中可以找到
在安装 xcode 请暂时关闭 VPN

二、打开VPN

三、安装 Command Line Tools
在 Mac 的 Spotlight 搜寻 Terminal
在终端机(Terminal)中输入指令:xcode-select --install
将会安装 command line tools ( 这是安装 Ruby 所需要的 Library)
如何确定已经装好?
在终端机输入:xcode-select -p
显示出:/Applications/Xcode.app/Contents/Developer
即是成功安装!

四、安装 Homebrew
可以从 Homebrew 取得安装 Ruby 需要一些编译工具或函式库。
在终端机输入:/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
按提示敲击 “return” 回车键,期间可以选择要不要输密码,没有就略过

五、使用 Homebrew 安装 Git
Git 是目前最流行的原始码版本控制软体
终端机里输入指令:brew install git
如何确定已经装好?
在终端机输入:git --version
会显示:git version 2.9.0(数字依照各版本可能会不同)

六、安装 ImageMagick
ImageMagick 是专门处理图片的函式库。
在终端机里输入指令:brew install imagemagick

七、安装 PostgreSQL
PostgreSQL 是一套强大的底层资料库。
在终端机里输入指令:brew install postgresql
安装完毕后:brew services start postgresql (会确保在你下次开机时,也一并启动资料库)

八、安装 RVM
因为 Ruby 版本更新很快,我们通常不会使用系统内建的 Ruby。而是会改采用一套 Ruby Version Manager 去管理 Ruby 的安装与升级。透过 Ruby Version Manager ( RVM) 去安装 Ruby。
请在终端机输入:\curl -sSL https://get.rvm.io | bash -s stable
安装完再输入:source ~/.rvm/scripts/rvm 让 rvm 生效。
然后安装一个套件,在终端机输入:brew install libxml2

九、安装 Ruby
一种程式语言,它经常和框架(Rails)结合,常用于快速开发。
请在终端机输入:rvm install 2.3.1
然后输入:rvm use 2.3.1 --default (使用 ruby 2.3.1 为预设版本 (可改自己需要的版本))
安装完成后需要重开 Terminal。(在 Terminal 界面按 command + Q 退出,然后重新打开)
输入 rvm list 可以列出目前已安装的 Ruby 版本。
一直无法连上怎么办?如果遇到连不太上 https://rubygems.org
可以指定 RubyGems 来源
方法:多执行這一行
gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/

十、安装 Rails 开发框架
在终端机输入:gem install rails -v 5.0.0 (后面的参数是指定版本,预设会抓最新版。)
安装失败怎么办?!
失败的可能原因是 gem 或 xcode-select 不是最新版本
可以使用这两个指令来更新
gem update --system
xcode-select --install

十一、Atom 编辑器
https://atom.io下载

十二、设定在 Terminal 内可以打开 Atom
如何在 Terminal 内可以“用惯用的编辑器”快速打开“目前正在做”的专案(project)呢?
启动功能 Shell commands


执行完上述动作要关闭Terminal重启才能生效(在 “3-9 安装 Ruby”有提示如何重启 Terminal)。

十三、在 Github 上设定自己的电子签名(如果有第二台mac最好也这么做)
步骤 1
在 Terminal 先输入 more ~/.ssh/id_rsa.pub 看看有没有东西。


有的话 copy 整个内容,到步骤 3。
没有的话到步骤 2。
步骤 2
如果没有 id_rsa.pub 这个档案, 请输入 ssh-keygen 连按三个回车(enter)

然后再输入一次 more ~/.ssh/id_rsa.pub , 把出来的内容 copy 起来
步骤 3
前往 https://github.com/settings/ssh
新增 ssh key

十四、调配Terminal为zsh与安装oh-my-zsh
第一:下载安装 iTerm2
到官方网址下载 iTerm:http://www.iterm2.com/

第二:调配 Terminal: zsh与oh-my-zsh

Step1:改变 shell 为 zsh 设置为默认shell
打开刚刚安装的 iTerm,在这个终端机里面输入:chsh -s /bin/zsh

Step2:安装 oh-my-zsh
在终端中分别输入以下指令:
cd ~/
git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh
cp ~/.zshrc ~/.zshrc.orig
cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc

Step3:修改 theme
在终端输入:atom .zshrc
Atom 会打开名为 “.zshrc” 的文件,把里面内容为以下的这样:
ZSH_THEME="robbyrussell"
修改替换成下面两行:

#ZSH_THEME="robbyrussell"

ZSH_THEME="agnoster"

并在最后添加:

alias stree='/Applications/SourceTree.app/Contents/Resources/stree'

export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8

Step4:修改 agnoster 的 theme source code
在终端输入:atom ~/.oh-my-zsh/themes/agnoster.zsh-theme
Atom 会打开名为 “agnoster.zsh-theme” 的文件。然后把这个文件里的全部内容替换成这个链接里面的内容。
https://gist.github.com/agnoster/3712874/raw/c3107c06c04fb42b0ca27b0a81b15854819969c6/agnoster.zsh-theme
存档(按 command + s)后关闭 Atom。

Step5:安装字型
因为 agnoster 需要特殊字型,所以必须安装 patched 过的三个字体。请去这个链接下载三个字体:https://gist.github.com/1595572
下载后安装这三个字体。

Step6: 替换布景
下载 solarized,解压缩后,进行iterm2的Preferences操作
替换掉 iTerm 布景
请在 iTerm 里按command+,(就是键盘上的逗号)打开 Preferences 对话框
Preferences -> Profiles -> Colors -> Color Presets -> Import,载入 iterm2-colors-solarized 目录下的两个 itermcolors
Preferences -> Profiles -> Colors -> Color Presets,选择 Solarized Dark
替换掉 iTerm 字体
Preferences -> Profiles -> Text -> Change Font换成 Menlo 字体,字号 14
按cmd+q退出,再打开,可以获得如下效果

十五、设置MAC终端命令行下用atom、sublime、vscode打开文件或目录
修改 zsh 配置文件
open ~/.zshrc
在文件中加上这几行代码
对应 atom、Sublime Text、与 vcCode。

alias atom='/Applications/Atom.app/Contents/MacOS/Atom'
alias subl='/Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl'
alias code='/Applications/Visual\ Studio\ Code.app/Contents/Resources/app/bin/code'

测试
分别使用 Atom、Sublime Text、与 vcCode 打开
atom .
subl .
code .

十六、安装heroku
brew install heroku/brew/heroku

***怎么用git clone 远程的所有分支

首先, clone 一个远端仓库,到其目录下:

git clone git://example.com/myproject
cd myproject

然后,看看你本地有什么分支:
git branch
会出现:
* master

但是有些其他分支你在的仓库里面是隐藏的,你可以加上-a选项来查看它们:
git branch -agit branch -r

* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/develop
  remotes/origin/feature
  remotes/origin/feature-im
  remotes/origin/master
  remotes/origin/newbranch

如果你想快速的使用上面的分支,可以直接切换到那个分支:
git checkout origin/feature

但是,如果你想在那个分支工作的话,你就需要创建一个本地分支:
git checkout -b feature origin/feature

或者一步到位的方法,使用-t参数,它默认会在本地建立一个和远程分支名字一样的分支
git checkout -t origin/feature

现在,如果你看看你的本地分支,你会看到:
git branch
会出现

* feature
  master

也可以使用fetch来做:
git fetch origin feature:feature或者更好的git fetch origin feature feature
不过通过fetch命令来建立的本地分支不是一个track branch,而且成功后不会自动切换到该分支上

克隆所有分支的方法
优先用git fetch --all
其次或用git pull --all
查看克隆下来的所有分支:
输入git checkouttab

最后可以用gitk查看做了些什么动作:
gitk --all &

 bundle install
 cp config/database.yml.example config/database.yml
rake db:migrate

如果不是单独clone,而是用的fork

举例:
和老师的仓库保持同步

因为自己和老师的仓库都是一直在更新,所以每次提交前,需要先将自己的仓库(包括远程和本地)同步为老师仓库的最新状态,然后再提交自己的更改。

首先,添加徐老师的仓库,并命名为upstream,方便以后再次调用。

git remote add upstream git@github.com:xugy0926/getting-started-with-javascript.git
然后将老师的仓库中的内容下载至本地。注意,下载至本地的内容,和自己的仓库是互不干扰的。

git remote -v查看自己跟踪的repo,确保正确无误
比如:

orgin    git@github.com:lichen19844/getting-started-with-javascript.git (fetch)
orgin    git@github.com:lichen19844/getting-started-with-javascript.git (push)
upstream git@github.com:xugy0926/getting-started-with-javascript.git (fetch)
upstream git@github.com:xugy0926/getting-started-with-javascript.git (push)

git fetch upstream master
这时,徐老师仓库的最新内容已经下载至本地了。

我们先不急着合并进来,先看看自己的仓库和老师的仓库有哪些不同。
git diff upstream/master
注意,执行git diff后,终端会进入vim环境,注意看图中最后一行行首的冒号。按一下q键,退出vim环境即可。

对比完徐老师和自己仓库的不同之后,就要把这些内容(upstream/master,代表upstream这个源的master分支)合并到自己的仓库中了。
git merge upstream/master

提交单个文件:
举例git add foo.txt 把名叫 foo.txt 的档案加进追踪修订。

附注:

添加远程仓库(origin 名称可根据需要添加)

git remote add origin <url>

删除(origin 名称需根据你本地查询出来的想删除的名字, 查询命令为 git remote -v)

git remote rm origin

给本地和远程仓库重命名

1.重命名本地分支
git branch -m new-name #如果当前在要重命名的分支
git branch -m old-name new-name #如果当前不在要重命名的分支

2.删除远程旧名称分支并且push新名称分支
git push origin :old-name new-name

3.关联新名称的本地分支和远程分支
git push origin -u new-name

修改远程仓库地址
git remote set-url origin [url]

分别查看仓库 local global system 的配置信息
git config --local --list
git config --global --list
git config --system --list

仓库配置增加用户
git config --local --add user.name yourname

***Github 协作开发操作说明

Github 协作开发操作说明

Github组队协作教程

说明:以下的操作都分为两个角色,这两个角色又统称为开发者:
主程手:建立Github repository并且邀请其他协作者的人
协作者:接受邀请共同开发repository的的协作者。

准备篇

主程手动作:

1.建立develop分支,并保证该分支最新

  • 如果本地没有develop分支,那么在本地新建develop分支git checkout -b develop
  • 如果本地之前建立过develop分支,目前该分支并不是最新的分支,要把最新的改动merge到该分支,处理方法如下:
    git checkout develop
    git merge 最新分支名
    
    2.把develop分支推送到Github 上一步完成后,develop分支应该是最新分支了,那么这一步要保证develop分支推送到Github。git push origin develop

    这里说明下,为什么要新建develop分支,而不是直接在master分支上进行合并的原因是:develop主要用于开发,而master要保证用于production环境,有一个develop做缓冲,可以确保不会因为提交了bug代码导致master爆炸。

    3.邀请队友

    前两步完成后,就可以邀请队友了,在Github上选中想要协作的repository,然后点击Settings -> Collaborators
    在页面中间的搜索框中输入队友github的username,如果找到,框下面会跳出队友头像,点击头像,然后点击 Add collaborator,这样就邀请成功啦。

    协作者(副程)动作:

    1.接受邀请

    如果上一步邀请成功,协作者的Github邮箱应该会收到一份邀请邮件:


    点击View invitation后,跳转到Github页面

    点击Accept invitation,会跳转到协作repository,同时收到一封确认邮件。这样就算完成了接受邀请这个动作。

    2.获取最新代码

    现在协作者也可以看到协作的repository了,我们要把Github上面的repository下载到本地,依次执行以下操作如下:

    git clone repository地址(就是指:主程的远程主机名)
    cd xxx(专案名)
    git fetch <主程的远程主机名> <我们要的分支名>
    git checkout <我们要的分支名>
    cp config/database.yml.example config/database.yml
    bundle install
    rake db:migrate
    

    执行完之后,运行rails s,看代码是否能跑起来。如果能跑起来这一步就OK了。

    共同开发篇

    开发者(主程手+协作者)动作:

    注意:不管是主程手还是协作者,在写新功能的时候,都要按照下面的方式操作。

    1. 从Github拉取最新的分支到本地

    我们每次开发的时候,都要先从Github拉取最新的develop分支到本地。这个是必须做的动作,切记。操作方法:
    先切换到develop分支git checkout develop,然后执行:git pull origin develop

    (关于这一点,最好是用git fetch origin develop:develop或者更好的git fetch origin develop develop,以防万一,因为pull自带merge合并的属性,有风险)

    如果没有最新的改动,会使这样的提示:

    2. 从develop分支上切一个新分支进行开发。

  • 确保develop分支最新后,需要切除一个新分支来进行开发,分支的命名规则必须是:name+feature(姓名-功能)
  • 在自己刚刚切出来的分支上进行开发,开发完成后,必须在本地测试通过,需录制GIF动图证明。注意将全部的改动加到git中:
    git add .
    git commit -m "这个分支做的功能"
    

  • 将刚刚的分支push到GitHub上,git push origin name-feature
  • 转到GitHub页面,创建pull request,具体步骤如下:

    点击Compare & pull request

    确认base后面的分支是develop(这里要特别注意),compare后面的分支是要上传的分支,然后点击Create pull request

    创建 pull request 成功,再次提示,协作者到这里就结束了,不是主程手,请不要点击Merge pull request!

    主程手动作:

    1.常规合并(Merge Pull Request)

    主程手点击Pull request应该可以看到所有的pull request 记录


    点开pull request,如果显示:This branch has no conflicts with the base branch,这种情况可以直接merge,点击Merge pull request,然后Confirm merge 就可以了

    Merge完成后图标会变成Merged,见下图:

    2. 处理冲突

    但是有些情况,是不能直接merge的,会提示This branch has conflicts that must be resolved,这时 Merge pull request 按钮呈灰色,无法点击,必须先解决代码冲突才能继续。


    解决冲突有两个办法:

  • 第一个是不处理这个pull request ,协作者在本地重新修改后再新建一个pull request。
  • 另外一种方法就是按照GitHub给出的指令处理冲突: 点击 Resolve conflicts,左边显示出代码有冲突的所有文件,打开其中一个文件

    可以看到<<<<<<<>>>>>>> master 把代码有冲突的部分包了起来,=======又把这部分分成了上下两半。 上半部分是当前pull request中这个文件的写法 下半部分是当前master分支上这个文件的写法 接下来把多出的三行符号删掉,保留你需要的代码 比如

    然后点击Mark as resolved 冲突全部解决之后,会显示 Resolved all conflicts,点击 Commit changes

    之后会跳转回当前pull request地址,显示 This branch has no conflicts with the base branch,点击 Merge pull request,然后 Confirm merge 就可以了。

    工具篇

    SourceTree

    资源
    (转载自胡奎)
    一些git索引
    Git远程操作详解
    组队协作补充

    git删除远程分支的方法:
    git push origin :branch_you_want_to_delete
    git push origin --delete branch_you_want_to_delete

  • ***iTerm2快捷键一览 & 命令行

    这里是 iTerm2 的基本入门操作,熟悉之后可以大幅提高效率。

    最少必要知识MAKE(“Minimal. Actionable. Knowledge. and. Experience”)

    光标控制

    ctrl + a: 到行首
    ctrl + e: 行末
    ctrl + w: 删除光标前的单词
    ctrl + u: 删除一行

    窗口操作

    新建窗口:shift + command + d(横向)
    command + d(竖向)
    进入和退出全屏: Command + Enter
    清屏(重置当前终端): Command + r

    标签页操作

    新建标签页: Command + T
    关闭标签页: Command + W

    使用鼠标

    好用的复制功能:选中即复制
    默认情况下,选取的文字会自动复制到剪切板

    删除文件:rm -r [filename] 删除一个文件(夹)

    <!--more-->
    

    (以下全文转载自小土刀的iTerm2 指南)

    全文如下:

    光标控制
    ctrl + a: 到行首
    ctrl + e: 行末
    ctrl + f/b: 前进后退,相当于左右方向键,但是显然比移开手按方向键更快
    ctrl + p: 上一条命令,相当于方向键上
    ctrl + r: 搜索命令历史,这个大家都应该很熟悉了
    ctrl + d: 删除当前字符
    ctrl + h: 删除之前的字符
    ctrl + w: 删除光标前的单词
    ctrl + k: 删除到文本末尾
    ctrl + t: 交换光标处文本
    ctrl + u: 删除一行
    ⌘ + —/+/0: 调整字体大小
    ⌘ + r:清屏,其实是滚到新的一屏,并没有清空。ctrl + l 也可以做到。
    窗口操作
    新建窗口:shift + command + d(横向)command + d(竖向)
    关闭窗口:shift + command + w
    前一个窗口:command + `
    后一个窗口:command + ~
    进入窗口 1,2,3:option + command + 编号
    标签页操作
    新建标签页: Command + T
    关闭标签页: Command + W
    前一个标签页: Command + 左方向键,Shift + Command + [
    后一个标签页: Command + 右方向键,Shitf + Command + ]
    进入标签页1,2,3…: Command + 标签页编号
    Expose 标签页: Option + Command + E(将标签页打撒到全屏,并可以全局搜索所有的标签页)
    面板操作
    垂直分割: Command + D
    水平分割: Shift + Command + D
    前一个面板: Command + [
    后一个面板: Command + ]
    切换到上/下/左/右面板: Option + Command + 上下左右方向键
    其他功能
    支持自定义全局快捷键用于显示和隐藏iTerm2 Preference -> Keys -> Show/hide iTerm2 with a system-wide hotkey 打上勾之后
    进入和退出全屏: Command + Enter
    查看当前终端中光标的位置: Command + /
    命令自动补全: Command + ;(很少用这个,还是感觉Zsh的补全更好用)
    开启和关闭背景半透明: Command + u
    清屏(重置当前终端): Command + r
    文本选取
    文本选取有使用鼠标和不使用鼠标两种方式。

    使用鼠标

    默认情况下,选取的文字会自动复制到剪切板,可以使用以下方式进行文本选取:

    常见的点击并拖拽方式
    双击选取整个单词
    三击选取整行
    选取某一部分,按住Shift,再点击某处,可以选取整个矩形内的文本(类似Windows下按住Shift可以批量选取图标)
    按住Command + Option,可以用鼠标画出一个矩形,用类似截图的方式选取文本 另外,还可以使用鼠标完成以下操作: 按住Command然后点击某个URL,会在浏览器中打开这个URL,点击某个文件夹,会在Finder里打开这个文件夹(再也不用open . 啦),点击某个文件名,会打开这个文件(文本文件支持MacVim,TextMate和BBEdit,如果后面跟随一个冒号和行号,文件会在行号处打开,其它格式的文件似乎不能调用默认程序打开) 选取文本之后,按住Command 同时拖动文本,可以将文本粘贴到目标位置(Drag and Drop) 鼠标中键粘贴(这个太感人了,一下子找回Linux的感觉了)
    不使用鼠标

    (这种方式最多只能选取一行文本) 使用 Command + f,会呼出一个搜索框,可以在当前面板中进行搜索,输入想要选取的部分内容,输入过程中,按Tab可以将选取部分向右扩展,按Shift + Tab向左扩展,按回车转到下一个匹配位置。使用Tab或Shift+Tab扩展得到想要的内容之后,选取内容会自动复制到剪切板,再次按Command + f隐藏搜索框。

    位置书签
    在当前会话中按Command + Shift + m可以保存当前位置,之后可以按Command + Shift + j跳回这个位置。

    粘贴历史
    使用Command + Shift + h 可以呼出粘贴历史,支持模糊检索。还可以设置将粘贴历史保存在磁盘上(Preferences -> General)

    即时回放
    使用Command + Opt + b 打开即时回放,按Esc退出。即时回放可以记录终端输出的状态,让你“穿越时间”查看终端内容。默认每个会话最多储存4MB的内容,可以在设置中更改(Preferences -> Genernal -> Instant Replay)。

    窗口状态
    通过 Window -> Save Window Arrangement 可以保存当前窗口状态的快照,包括打开的窗口,标签页和面板。通过 Window -> Restore Window Arrangement 还原。还可以在 Preferences -> General -> Open saved window arrangement 中设置在启动iTerm2时自动恢复窗口状态

    特色功能
    command+; 根据上下文呼出自动完成窗口,上下键选择
    全屏 command+enter
    光标去哪了?command+/
    用鼠标选中某个路径或者某个词汇就自动复制

    Read on

    ***Day136:利用Github 做第二遍(克隆专案同样可以参考)

    利用Github 做第二遍

    建立新目录,Clone专案
    打开终端,在根目录下,建立一个叫 job2 的子目录,进入该目录,从自己的github上clone原job-listing

    mkdir job2
    cd job2
    git clone git@github.com:你的github用户名/job-listing.git
    

    进入 job-listing 子目录,检查文件是否齐全

    cd job-listing
    ls
    
    git fetch <原来的远程主机名> <我们要拷下来的分支名>     #这步视情况可以不做
    git checkout <我们要拷下来的分支名>             #这步视情况可以不做
    

    其中<原来的远程主机名> 就是 git@github.com:你的github用户名/job-listing.git
    克隆下来的名字可以更改,最好更改成新repo同名,便于管理
    建立github上的新仓库
    前往自己的github,建立一个新的repo,可以取名叫 job-listing-2

    进入新仓库,选第二部分的代码,复制,为本地新专案上传做准备

    去除旧连接,建立新连接
    在本地新专案里,检查和github的连接(仓库)地址。然后删除这个地址,再检查一下发现电脑已经无github地址可报告。再粘贴刚才从github的 job-listing-2 仓库复制的命令,实现本地新专案上传到github新仓库。

    依次输入

    git remote -v
    git remote rm origin
    git remote -v
    git remote add origin git@github.com:你的github用户名/job-listing-2
    git push -u origin master
    

    上传成功,又可以开始新的征程了。
    然后更重要的是,如果这次新的专案已经有一些套件或新增栏位的话,那就严格执行下列命令。

    cp config/database.yml.example config/database.yml
    bundle check
    bundle install
    rake db:migrate
    rails s
    

    【JS】call()和apply()以及bind()方法

    call和apply的说明
    1、call,apply都属于Function.prototype的一个方法,它是JavaScript引擎内在实现的,因为属于Function.prototype,所以每个Function对象实例(就是每个方法)都有call,apply属性。既然作为方法的属性,那它们的使用就当然是针对方法的了,这两个方法是容易混淆的,因为它们的作用一样,只是使用方式不同。
    2、语法:foo.call(this, arg1,arg2,arg3) == foo.apply(this, arguments) == this.foo(arg1, arg2, arg3);
    3、相同点:两个方法产生的作用是完全一样的。
    4、不同点:方法传递的参数不同。

    function A(){
        this.flag = 'A';
        this.tip = function(){
            console.log(this.flag);
        };
    }
    function B(){
        this.flag = 'B';
    }
    var a = new A();
    var b = new B();
    a.tip()                    //A
    a.tip.call(b);  //B
    a.tip.apply(b); //B
    // 从结果中可以看出callapply都可以让B对象调用A对象的tip方法,并且修改了this的当前作用对象为b
    
    function Add(a,b) {  
        console.log(a+b);  
    }  
    function Sub(a,b) {  
        console.log(a-b);  
    }  
    Add.call(Sub,3,1);   //4
    // callapply都能修改当前作用对象Add为对象Sub,随即传递参数31,调用对象Add方法
    
    function Product(name, price) {
        this.name = 'cake'
        this.price = 10
    }
    function Food(name, price) {
        // Product 函数内部的 this  Food  this
        this.name = 'nuddle'
        // ❤️对象food调用了对象product,并修改了this的当前作用对象productfood,随即这里的参数nameprice传参给对象product内使用,会变成this.name  this.price
        Product.call(this, name, price) 
    }
    var product = new Product()
    var food = new Food('cheese', 5)
    // console.log(new Food('cheese', 5).name)
    console.log(food.name)  //"cake"
    
    function sum(num1, num2) { 
        return num1 + num2; 
        } 
    console.log(sum.call(this, 10, 10)); //20 
    console.log(sum.apply(this,[10,20])); //30 
    
    var firstName = "diz"; 
    var lastName = "song"; 
    var myObject = { firstName: "my", lastName: "Object" }; 
    function HelloName() { 
      console.log("Hello " + this.firstName + " " + this.lastName, " glad to meet you!"); 
    } 
    HelloName.call(this); //Hello diz song glad to meet you!  
    HelloName.call(myObject); //Hello my Object glad to meet you!
    

    apply()和call()的真正用武之地是能够扩充函数赖以运行的作用域,如果我们想用传统的方法实现,请见下面的代码:
    代码如下:

    传统方法
    var firstName = "diz"; 
    var lastName = "song"; 
    var myObject = { firstName: "my", lastName: "Object" }; 
    function HelloName() { 
    console.log("Hello " + this.firstName + " " + this.lastName, " glad to meet you!"); 
    } 
    HelloName(); //Hello diz song glad to meet you! 
    myObject.HelloName = HelloName; 
    myObject.HelloName(); //Hello my Object glad to meet you! 
    

    传统方法:
    要想让HelloName()函数的作用域在对象myObject上,我们需要动态创建myObject的HelloName属性,此属性作为指针指向HelloName()函数,这样,当我们调用myObject.HelloName()时,函数内部的this变量就指向myObjecct,也就可以调用该对象的内部其他公共属性了。

    apply()方法

    function sum(num1, num2) { 
    return num1 + num2; 
    } 
    console.log(sum.call(this, 10, 10)); //20 
    console.log(sum.apply(this,[10,20])); //30 
    
    var array = ['a', 'b']
    var elements = [0, 1, 2]
    array.push.apply(array, elements)   // 结合push方法,["a", "b", 0, 1, 2]
    // array.push.call(array, elements) // [ 'a', 'b', [ 0, 1, 2 ] ]
    // array.push(elements)             // [ 'a', 'b', [ 0, 1, 2 ] ]
    console.info(array); 
    
       function Animal(){    
            this.name = "Animal";    
            this.showName = function(){    
                console.log(this.name);    
            }    
        }    
        function Cat(){    
            this.name = "Cat";    
        }    
        var animal = new Animal();    
        var cat = new Cat();    
        //通过callapply方法,将原本属于Animal对象的showName()方法交给对象cat来使用了。    
        //输入结果为"Cat"    
        animal.showName.call(cat,",");    
        //animal.showName.apply(cat,[]);  
    

    bind()方法

    bind()方法
    color = "red"; 
    var o = { color: "blue" }; 
    function sayColor(){ 
    console.log(this.color); 
    } 
    var OSayColor = sayColor.bind(o); 
    var RsayColor = sayColor;
    RsayColor();  //red
    OSayColor(); //blue
    

    参考:
    https://www.w3cschool.cn/xqw2e7/9m2x12y0.html

    【JS】this

    一语中的: 在 JavaScript 中,this 是在调用函数时确定的,而不是在创建函数时确定的。

    调用普通函数

    创建一个 js 文件,并且定义一个普通的函数 foo,然后调用。

    function foo() {
     console.log(this) // global object
    }
    foo()
    
    > Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > undefined      //严格模式下自动输出 undefined
    

    原则一 如果一个函数只是作为普通的调用,那么函数体内的 this 将都是全局对象,如果在严格模式下则为 undefined。

    function foo() {
        console.log(this)
         function bar() {
       console.log(this) // global object
     }
    
     bar()
    }
    foo()
    
    // 输出 global object
    > Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > undefined        //严格模式下自动输出 undefined
    

    虽然 bar 定义在 foo 体内,但是 bar 的调用也是一个普通的调用,bar 内的 this 还是全局对象。

    调用对象的方法

    原则二 如果通过对象来调用函数,该函数内的 this 则是调用对象本身。

    function Person() {
     this.age = 18
     console.log('Person函数内this 指向 ', this)
     this.name = 'young'
     console.log('Person函数内this 指向 ', this)
     this.info = function() {
         console.log('this.info函数内的 this 指向 ', this)
       return { 
         age: this.age, 
         name: this.name
       }
     }
     this.info()   //❤️注意这个调用
     console.log('Person函数内this 指向 ', this)
    }
    var p = new Person()        //对象p实例化后,直属于该函数内的console.log会马上执行
    console.log('对象p 是 ', p)
    console.log('p.info() 是 ', p.info())
    
    > Person函数内this 指向  Person {age: 18}
    > Person函数内this 指向  Person {age: 18, name: "young"}
    > this.info函数内 this 指向  Person {age: 18, name: "young", info: [Function]}
    > Person函数内this 指向  Person {age: 18, name: "young", info: [Function]}
    > 对象p   Person {age: 18, name: "young", info: [Function]}
    > this.info函数内 this 指向  Person {age: 18, name: "young", info: [Function]}
    > p.info()   {age: 18, name: "young"}
    > undefined
    Person() //执行Person()
    > Person函数内this 指向  Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > Person函数内this 指向  Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > this.info函数内 this 指向  Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > Person函数内this 指向  Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    undefined
    

    1、var p = new Person() 对象实例化,直属于该函数内的console.log会马上执行,此时(运行时)函数Person内的this指向了实例化后的对象p
    2、如果通过对象来调用函数,该函数内的 this 则是调用对象本身,即函数 info 是通过对象 p 来调用的,所以 info 内的 this 是对象 p。最终结果为 { age: 18, name: 'young' }

    我们来“恶意”修改 info 的调用方式。
     // age = 188
     // name = 'young88'
     
    function Person() {
     this.age = 18
     this.name = 'young'
     this.info = function() {
       console.log('this.info函数内的 this 指向 ', this)
       return { 
         age: this.age, 
         name: this.name
       }
     }
     this.info()
    }
    var p = new Person()    //对象p实例化后,函数内的console.log会马上执行
    console.log('对象p 是 ', p)
    console.log('p.info() 是 ', p.info())
    var devil = p.info
    console.log(devil())
    
    //    p
    > this.info函数内的 this 指向  Person {age: 18, name: "young", info: ƒ}
    > 对象p   Person {age: 18, name: "young", info: ƒ}
    > this.info函数内的 this 指向  Person {age: 18, name: "young", info: ƒ}
    > p.info()   {age: 18, name: "young"}
    //  devil
    > ❤️this.info函数内的 this 指向  Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …} //devil
    > {age: undefined, name: undefined}
    

    看区别:
    var p = new Person() 是对象p实例化
    var devil = p.info 只是赋值,并没有new,所以没有对象实例化,devil只是一个普通函数,等同于:

    function devil () {
       console.log('函数内的 this 指向 ', this)
       return { 
         age: this.age, 
         name: this.name
       }
     }
     console.log(devil())
    
    > ❤️函数内的 this 指向  Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > {age: undefined, name: undefined}
    

    虽然 info 的定义方式没变,但是改变了调用方式。devil() 是一次普通函数的调用,this 就是全局对象了。
    1、全局对象 this 如果没有变量 age 和 name,所以打印均为 undefined。最终结果为 { age: undefined, name: undefined }。
    2、全局对象 this 如果有变量 age 和 name,打印结果为 { age: 188, name: "young88" }。

    这种“恶意”修改 info 的调用方式在平时屡见不鲜。比如,你会把函数 info 注册给外部去回调。既然是回调,你就无法确认回调执行的方式,从而无法确保 this。

    我们来模拟一个异步回调。
    第1种
    function fetchAvator(print) {
        console.log('fetchAvator this指向 ', this)
        setTimeout(
            function() {
                print("image")
            }
        , 1000)
    }
       
    function Person() {
        this.age = 18
        this.name = "young"
        this.job = 'artist'
    
        this.info = function() {
                        console.log('this.info函数内的 this 指向 ', this)
                        fetchAvator(
                            function print(avator) {
                                console.log('print this指向 ', this)
                                console.log({ 
                                    age: this.age, 
                                    name: this.name, 
                                    job: this.job,
                                    avator
                                })
                            }
                        )
                    }
    }
       
    var p = new Person()
    p.info()
    
    > this.info函数内的 this 指向  Person {age: 18, name: "young", job: "artist", info: ƒ}
    > fetchAvator this指向  Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > undefined
    > print this指向  Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > {age: undefined, name: undefined, job: undefined, avator: "image"}
    

    相当于:

    function Person() {
        this.age = 18
        this.name = "young"
        this.job = 'artist'
    
        this.info = function() {
                        console.log('this.info函数内的 this 指向 ', this)
                        // var that = this
                        function fetchAvator(print) {
                            console.log('fetchAvator this指向 ', this)
                            setTimeout(
                                function() {
                                    print("image")
                                }
                            , 1000)
                        }
    
                        fetchAvator(
                            function print(avator) {
                                console.log('print this指向 ', this)
                                console.log({ 
                                    age: this.age, 
                                    name: this.name, 
                                    job: this.job,
                                    avator
                                })
                            }
                        )
                    }
    }
       
    var p = new Person()
    p.info()
    
    > this.info函数内的 this 指向  Person {age: 18, name: "young", job: "artist", info: ƒ}
    > fetchAvator this指向  Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > undefined
    > print this指向  Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > {age: undefined, name: undefined, job: undefined, avator: "image"}
    

    改一下:

    第2种 在function fetchAvator内部加this
    function fetchAvator(print) {
        this.age = 29
        this.name = 'xu'
        this.job = 'coder'
        console.log('fetchAvator this指向 ', this)
        setTimeout(
            function() {
                print("image")
            }
        , 1000)
    }
       
    function Person() {
        this.age = 18
        this.name = "young"
        this.job = 'artist'
    
        this.info = function() {
                        console.log('this.info函数内的 this 指向 ', this)
                        fetchAvator(
                            function print(avator) {
                                console.log('print this指向 ', this)
                                console.log({ 
                                    age: this.age, 
                                    name: this.name, 
                                    job: this.job,
                                    avator
                                })
                            }
                        )
                    }
    }
       
    var p = new Person()
    p.info()
    
    > this.info函数内的 this 指向  Person {age: 18, name: "young", job: "artist", info: ƒ}
    > fetchAvator this指向  Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > undefined
    > print this指向  Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > {age: 29, name: "xu", job: "coder", avator: "image"}
    

    fetchAvator 函数是一个模拟异步的函数,在函数 info 内,会调用 fetchAvator 并等回调 print。
    这里最关键的问题是无法决定 print 的回调方式,从而也就无法决定 print 体内的 this。
    在上面例子中,print() 是一次普通的函数调用,所以 print 体内的 this 是全局对象。
    上面代码执行结果为:{ age: undefined, name: undefined, avator: 'image' }。
    ❤️为了保证在 print 体内能访问到 Person 对象 p ,可以在函数 info 中先让 this 保护起来。

    再改一下:

    第3种 注意that在不同的位置导致不同结果
    function fetchAvator(print) {
        this.age = 29
        this.name = 'xu'
        this.job = 'coder'
        console.log('fetchAvator this指向 ', this)
        setTimeout(
            function() {
                print("image")
            }
        , 1000)
    }
       
    function Person() {
        this.age = 18
        this.name = "young"
        this.job = 'artist'
    
        this.info = function() {
                        var that = this //❤️
                        console.log('this.info函数内的 this 指向 ', this)
                        fetchAvator(
                            function print(avator) {
                                console.log('print this指向 ', this)
                                // var that = this
                                console.log({ 
                                    age: that.age, 
                                    name: that.name, 
                                    job: that.job,
                                    avator
                                })
                            }
                        )
                    }
    }
       
    var p = new Person()
    p.info()
    
    > this.info函数内的 this 指向  Person {age: 18, name: "young", job: "artist", info: ƒ}
    > fetchAvator this指向  Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > undefined
    > print this指向  Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > {age: 18, name: "young", job: "artist", avator: "image"}
    
    第4种 注意that在不同的位置导致不同结果
    function fetchAvator(print) {
        this.age = 29
        this.name = 'xu'
        this.job = 'coder'
        console.log('fetchAvator this指向 ', this)
        setTimeout(
            function() {
                print("image")
            }
        , 1000)
    }
       
    function Person() {
        this.age = 18
        this.name = "young"
        this.job = 'artist'
    
        this.info = function() {
                        // var that = this
                        console.log('this.info函数内的 this 指向 ', this)
                        fetchAvator(
                            function print(avator) {
                                console.log('print this指向 ', this)
                                var that = this //❤️
                                console.log({ 
                                    age: that.age, 
                                    name: that.name, 
                                    job: that.job,
                                    avator
                                })
                            }
                        )
                    }
    }
       
    var p = new Person()
    p.info()
    
    > this.info函数内的 this 指向  Person {age: 18, name: "young", job: "artist", info: ƒ}
    > fetchAvator this指向  Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > undefined
    > print this指向  Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}
    > {age: 29, name: "xu", job: "coder", avator: "image"}
    

    Function.prototype.bind()

    原则三 如果函数通过 bind 绑定了一个对象,那么函数的 this 永远是 bind 方法提供的对象。

    bind() 方法创建一个新的函数,当这个新函数被调用时 this 为其提供的值,其参数列表前几项值为创建时指定的参数序列。
    上面例子还可以用 bind 来解决问题。

    使用bind
    function Person() {
     this.age = 18
     this.name = 'young'
    
     this.info = function() {
       fetchAvator(function print(avator) {
         console.log({
           age: this.age, 
           name: this.name, 
           avator
         })
       }.bind(this))
     }
    }
    

    箭头函数

    原则四 箭头函数会自动绑定上下文的 this

    箭头函数其实就是隐式地调用了 bind() 并绑定当前上下文的 this。你可以简单的认为箭头函数其实 bind 的一种简写方式。

    箭头函数
    function Person() {
     this.age = 18
     this.name = 'young'
    
     this.info = function() {
       fetchAvator((avator) => {
         console.log({
           age: this.age, 
           name: this.name, 
           avator
         })
       })
     }
    }
    

    Function.prototype.call() & Function.prototype.apply()

    原则五 如果用 call 或者 apply 来调用函数,this 则为 call 或 apply 的第一个参数对象。

    call() 方法调用一个函数,会给定 this,并按照参数列表方式提供的参数。
    apply() 方法调用一个函数,会给定 this ,并按照数组提供参数。
    他两的共同点是会给定一个 this,不同点是给定参数的方式不一样。

    function Product(name, price) {
        this.name = 'cake'
        this.price = 10
       }
       
    function Food(name, price) {
        // ❤️对象food调用了对象product,并修改了this的当前作用对象foodproduct,随即这里的参数nameprice对应了对象product内的同名参数this.name  this.price
        Product.call(this, name, price) 
        // Product 函数内部的 this  Food  this
        this.category = 'food'
    }
    var product = new Product()
    var food = new Food('cheese', 5)
    // console.log(new Food('cheese', 5).name)
    console.log(food.name)
    // output: "cake"
    
    var array = ['a', 'b']
    var elements = [0, 1, 2]
    array.push.apply(array, elements)
    console.info(array); // ["a", "b", 0, 1, 2]
    

    这段代码巧妙的利用了 apply 实现 Array.push 支持 插入一个数组。
    不管是 call(),还是 apply(),在调用时给定的 this 决定了函数的 this 值。
    语法:
    foo.call(this, arg1,arg2,arg3) == foo.apply(this, arguments) == this.foo(arg1, arg2, arg3);

    【JS】js中多个小括号()()的用法

    有时我们看见js函数后面跟着多个小括号是怎么回事?f( )( )( )...

    f()意思是执行f函数,返回子函数

    f()()执行子函数,返回孙函数

    f()()()执行孙函数

    ... ...

    但注意,如果想这样执行,函数结构必须是这样,f的函数体里要return 子函数,子函数里要return 孙函数,如果没有return关键字,是不能这样连续执行的,会报错的。

    举个例子:k是f的子函数,return子函数k,所以f()()能连续执行。

    var i=1;
    
    function f(){
        i++;
        console.log("-------------  :  "+i)
        return k;
        function k() {
            return "I love you!"
        }
    }
    
    f();
    
    f()();
    
    alert(f()());
    

    参考:
    https://blog.csdn.net/claram/article/details/6424432
    https://blog.csdn.net/neymar_jr/article/details/79119910

    【html css】keyframes

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8"> 
    <title>W3Cschool教程(w3cschool.cn)</title>
    <style> 
    .spinner {
        width: 40px;
        height: 40px;
        position: relative;
    }
    
    .double-bounce1,
    .double-bounce2 {
        width: 100%;
        height: 100%;
        border-radius: 50%;
        background-color: #3063b2;
        opacity: 0.6;
        position: absolute;
        top: 0;
        left: 0;
        -webkit-animation: bounce 2.0s infinite ease-in-out;
        animation: bounce 2.0s infinite ease-in-out;
    }
    
    .double-bounce2 {
        -webkit-animation-delay: -1.0s;
        animation-delay: -1.0s;
    }
    
    @-webkit-keyframes bounce {
    
        0%,
        100% {
            -webkit-transform: scale(0.0)
        }
    
        50% {
            -webkit-transform: scale(1.0)
        }
    }
    
    @keyframes bounce {
    
        0%,
        100% {
            transform: scale(0.0);
            -webkit-transform: scale(0.0);
        }
    
        50% {
            transform: scale(1.0);
            -webkit-transform: scale(1.0);
        }
    }
    </style>
    </head>
    <body>
    
    <p><strong>注意:</strong>@keyframes不兼容IE 9 and 以及更早版本的浏览器.</p>
    
    <div class="spinner">
    <div class="double-bounce1"></div>
    <div class="double-bounce2"></div>
    </div>
    
    </body>
    </html>
    

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8"> 
    <title>W3Cschool教程(w3cschool.cn)</title>
    <style> 
    div
    {
        width:100px;
        height:100px;
        background:red;
        position:relative;
        animation:mymove 5s infinite;
        -webkit-animation:mymove 5s infinite; /* Safari and Chrome */
    }
    
    @keyframes mymove
    {
        0%   {top:0px; left:0px; background:red;}
        25%  {top:0px; left:100px; background:blue;}
        50%  {top:100px; left:100px; background:yellow;}
        75%  {top:100px; left:0px; background:green;}
        100% {top:0px; left:0px; background:red;}
    }
    
    @-webkit-keyframes mymove /* Safari and Chrome */
    {
        0%   {top:0px; left:0px; background:red;}
        25%  {top:0px; left:100px; background:blue;}
        50%  {top:100px; left:100px; background:yellow;}
        75%  {top:100px; left:0px; background:green;}
        100% {top:0px; left:0px; background:red;}
    }
    </style>
    </head>
    <body>
    
    <p><strong>注意:</strong>@keyframes不兼容IE 9 and 以及更早版本的浏览器.</p>
    
    <div></div>
    
    </body>
    </html>
    

    【小程序】 锁的概念

    把锁理解为一个变量,通过这个变量给它赋值来解决加载数据问题。比如上拉加载数据时,用户重复操作太快导致数据重复。

    关于“重复加载数据”,当用户上拉时,进行HTTP请求,当HTTP请求还没有返回的时候,用户马上又进行上拉,这时候,又会进行HTTP请求,而这次请求和上一次请求数据一致,也就导致了数据的重复。
    为了解决这个问题,有了锁的概念,也就是在第一次请求前,将这个锁(变量)赋值为true,请求成功或者失败时,将它赋值为false。然后在整个函数的外边判断只有false的时候才进行HTTP请求。这是前端编程的常用技巧。

    data:{
        loading: false
    },
    
    methods: {
        _load_more() {
    
          // 如果在加载完成前,发送了2个以上的请求,要求一次只发送一个请求
          // 
          if(this.data.loading) {
            return
          }
          // 锁住
          this.data.loading = true;
          bookModel.search(length, this.data.text)
          .then(res => {
            // 老数据 this.data.dataArray
            // 新数据 res.books
            const tempArray = this.data.dataArray.concat(res.books)
            this.setData({
              dataArray: tempArray,
              // loading: false
            })
            // 解锁
            this.data.loading = false;
          })
        }
    }