GithubHelp home page GithubHelp logo

wscats / articles Goto Github PK

View Code? Open in Web Editor NEW
3.2K 3.2K 734.0 93 KB

🔖My Learning Notes and Memories - 分享我的学习片段和与你的回忆

Home Page: https://github.com/Wscats/articles

Lua 100.00%
angular article blog document font-end node omi omil react tutorial vue

articles's People

Contributors

autumnswind avatar ctolib avatar wscats avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

articles's Issues

Angular源码解读setupModuleLoader函数

setupModuleLoader其实看字面意思就可以知道它为模板加载器,就是为module设置加载器
function setupModuleLoader(window) {}
首先传入了window对象,作用为为后面ensure判断window全局对象是否含有属性angular

function ensure(obj, name, factory) {
            return obj[name] || (obj[name] = factory())
        }

这里有个闭包函数,作用是判断对象中是否存在某个属性,没有的话创建对应的方法,并执行该方法
所以下面就会看到
ensure(ensure(window, "angular", Object), "module", function() {})
第一个ensure创建了为window对象创建angular
window.angular = Object()
第二个ensure创建了为angular对象创建module
到此步往下再看其实就变成了这样了
angular.module = function module(name,require,configFn);

  • name:字符串类型,代表模块的名称;
  • requires:字符串的数组,代表该模块依赖的其他模块列表,如果不依赖其他模块,用空数组即可;
  • configFn:用来对该模块进行一些配置。

这里就是我们常用到的模块加载部分
当我们angular.module('wscatApp'),只传一个参数,为getter操作,返回moduleInstance
当我们angular.module('wscatApp',[]) ,传了requires数组(空数组也行),为setter操作,也是返回对象moduleInstance
这两者有什么不同呢,可以看到module方法可以传递三个参数第二个参数require就是我们需要注入的依赖数组,其实就是注入所谓的DI

什么是DI呢,DI就是依赖注入
详情可以看这篇文章阐述
Javascript DI!Angular依赖注入的实现原理
截取里面几句话,方便这里的理解

  • 根据DI的原理,一个自然的推论就是:被注入的对象通常都是单例,因为创建了一个,就可以始终使用它了,不需要多次创建。
  • 在Angular中,所有主要编程元素都需要通过某种方式注册进去,比如myModule.service('serviceName', function()....这实际上就是把后面这个函数加入到一个容器中,要注意的是:angular全面实现了延迟初始化,也就是说,当这个对象没有被别人需要的时候,它是不会被创建的,这样对于提高性能有一定的帮助,特别是加快了启动速度。

这里的模块包括控制器、服务、过滤器、指令等子元素组成的整体
具体看源码这部分

moduleInstance = {
                            _invokeQueue: invokeQueue,
                            _runBlocks: runBlocks,
                            requires: requires,
                            name: name,
                            provider: invokeLater("$provide", "provider"),
                            factory: invokeLater("$provide", "factory"),
                            service: invokeLater("$provide", "service"),
                            value: invokeLater("$provide", "value"),
                            constant: invokeLater("$provide", "constant", "unshift"),
                            animation: invokeLater("$animateProvider", "register"),
                            filter: invokeLater("$filterProvider", "register"),
                            controller: invokeLater("$controllerProvider", "register"),
                            directive: invokeLater("$compileProvider", "directive"),
                            config: config,
                            run: function(block) {
                                return runBlocks.push(block), this
                            }
                        };

再往下看,里面有个闭包函数

var assertNotHasOwnProperty = function(name, context) {
                    if ("hasOwnProperty" === name) throw ngMinErr("badname", "hasOwnProperty is not a valid {0} name", context)
                };

这句也挺可爱的,module名称是不能以hasOwnProperty命名,否则会抛出”badname“的错误提醒。
下面继续执行
assertNotHasOwnProperty(name, "module"), requires && modules.hasOwnProperty(name) && (modules[name] = null), ensure(modules, name, function() {})
由于这里有多个异或操作,其实简单看起来就是进行了如下操作

assertNotHasOwnProperty(name, 'module');
      if (requires && modules.hasOwnProperty(name)) {
        modules[name] = null;
      }
      return ensure(modules, name, function() {})

这里很容易看出来,如果有传入requires数组(就算是空数组)还有modules对象里面有已定义好的对象或者属性,则会删除已有的module信息,将其置为null,并重新定义该属性

这里注意的是对比的modules对象,是在这个里面的
var modules = {};
它并不是全局的,而是只是在闭包**function(name, requires, configFn){}**局部作用域的。但由于是在闭包里面,所以它会一直保存在作用域中,相当于一直被缓存,不会被释放掉。
继续往下面看
if (!requires) throw $injectorMinErr("nomod", "Module '{0}' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.", name);
而requires如果为空,则表示是获取已有的模块
但如果requires不是空而是根本不存在,也就是说没有定义过这个module

看到上面这部分源码后启示我们在创建模块时,就算没有依赖其他模块,写法也应该是:
angular.module('wscatApp', []);
否则会到这里报错
angular.module('wscatApp',[...])和angular.module('wscatApp')
区别在于

  • angular.module('wscatApp',[...])会创建一个新的Angular模块,然后把方括号([...])中的依赖列表加载进来;
  • 而angular.module('wscatApp')会使用由第一个调用定义的现有的模块。

到这一步就是介绍这部分的最后一个闭包函数

function invokeLater(provider, method, insertMethod) {
                        return function() {
                            return invokeQueue[insertMethod || "push"]([provider, method, arguments]), moduleInstance
                        }
                    }

invokeLater从字面意思不难看出是之后再调用
理解这里我决定用一段简单的代码来阐述

var app = angular.module('wsscat', []);
        app.controller('serCtrl', function($scope, cat) {
            $scope.arr = cat.arr
        })

实际上我们还可以这样写

var app = angular.module('wsscat', []);
app.controller('serCtrl', ['$scope', 'cat', function($scope, cat) {
            //$scope.arr = cat.arr
            $scope.arr = "wsscat"
        }])

app.controller('serCtrl', function($scope, cat) {})
的controller运行了moduleInstance对象中的的invokeLater函数
controller: invokeLater("$controllerProvider", "register")
invokeLater传递三个参数

  • 第一个我们传入了$controllerProvider对应了provider
  • 第二个我们传入了register对应了method
  • 第三个我们传入了'serCtrl', function($scope, cat) {$scope.arr = cat.arr}对应了arguments

所以实际上变成了这样invokeQueue.push()

invokeQueue.push([$controllerProvider,register,['$scope', 'cat', function($scope, cat) {
            //$scope.arr = cat.arr
            $scope.arr = "wsscat"
        }]])

invokeQueue[insertMethod || "push"]([provider, method, arguments])
invokeQueue默认队列操作是push,如果有传入其他insertMethod
constant: invokeLater("$provide", "constant", "unshift")
则是unshift操作
animation: invokeLater("$animateProvider", "register")
则是register操作
由于return invokeQueue[insertMethod || "push"]([provider, method, arguments]), moduleInstance最后执行队列存放之后,会返回invokeQueue,所以后面可以进行链式操作,例如这样
app.controller().factory()

其他的模块和实例如下
举一反三,这里就不继续展开写了,引入AngularJS模块详解写的列表,如下

  • requires
  • 表示模块的依赖数组,即angular.module方法的requires参数。

angular模块实例属性和方法
属性
name
模块的名字。

  • _invokeQueue、_configBlocks、_runBlocks
  • 分别对应invokeQueue、configBlocks、runBlocks。

方法
模块的以下方法最后全部会返回模块实例本身,形成执行链。

  • animation()
  • 调用这个方法表示这个模块将在$animateProvider中注册一个动画服务。
  • 原理:在invokeQueue尾部插入['$animateProvider', 'register', arguments]。
  • config()
  • 调用这个方法,表示给这个模块追加一个配置方法。
  • 原理:在configBlocks尾部插入['$injector','invoke', arguments]。
  • constant()
  • 调用这个方法表示这个模块将给默认的$provider注册一个常量。
  • 原理:在invokeQueue首部插入['$provide', 'constant', arguments]。
  • controller()
  • 调用这个方法表示模块将在$controllerProvider中注册一个控制器。
  • 原理:在invokeQueue尾部插入['$controllerProvider', 'register', arguments]。
  • directive()
  • 调用这个方法表示这个模块将在$compileProvider中注册一个指令。
  • 原理:在invokeQueue尾部插入['$compileProvider', 'directive', arguments]。
  • factory()
  • 调用这个方法表示这个模块中将生成一个服务工厂(隐式创建一个了服务提供商)。
  • 原理:在invokeQueue尾部插入['$provide', 'factory', arguments]。
  • filter()
  • 调用这个方法表示这个模块将在$filterProvider中注册一个过滤器。
  • 原理:在invokeQueue尾部插入['$filterProvider', 'register', arguments]。
  • provider()
  • 调用这个方法表示这个模块将添加一个服务提供商。
  • 原理:在invokeQueue尾部插入['$provide', 'provider', arguments]。
  • run(block)
  • 调用这个方法表示这个模块将执行某个功能块,block可以是方法,也可以是数组。
  • 原理:在invokeQueue尾部插入block。
  • service()
  • 调用这个方法表示这个模块将注册一个服务(隐式创建了一个服务提供商)。
  • 原理:在invokeQueue尾部插入['$provide', 'service', arguments]。
  • value()
  • 调用这个方法表示这个模块将注册一个变量(隐式创建了一个服务提供商)。

看最后一句
return configFn && config(configFn), moduleInstance
这句转化成这样容易点理解

if (configFn) {
          config(configFn);
        }

也就是如果声明module时存在第三个参数,那么就把第三个参数的设置放进invokeQueue队列中。这样,就可以在载入一个module的时候同时做一些其他的事情。
config = invokeLater("$injector", "invoke")
config(configFn)
invokeLater("$injector", "invoke")(configFn)

CI 框架REST写API

首先我们去官网下载最新的CI框架下载 CodeIgniter,放到服务器安装,安装成功后,我们再去下载codeigniter-restserver,把下载回来的codeigniter-restserver文件夹中的application/libraries/Format.php and application/libraries/REST_Controller.php放到CI框架的application/libraries里面

这里写图片描述

然后我们在控制器中welcome.php的头部添加一句,以后我们现在那个控制器里面做接口就可以对应在那个控制器头部引入REST_Controller.php就可以了

还要把extends指向REST_Controller,因为REST_Controller本来是继承于CI的控制器的,然后我们就可以调用里面的方法了

require_once APPPATH . '/libraries/REST_Controller.php';
class Welcome extends REST_Controller {}

我运行的时候提示了我缺少了rest_controller_lang.php,我把rest_controller_lang.php文件也顺便加进去了
这里写图片描述
我们就可以这样来写写接口的例子了

public function index_get() {
        $format = $this -> get('format');
        // GET param
        $user = $this -> post('user');
        // POST param
        $age = $this -> put('age');
        // PUT param
        $this -> response(['format'=>$format,'user'=>$user, 'age'=>$age]);
    }

    public function index_post() {
    }

在网站输入以下网址请求数据
http://127.0.0.1/wsscat/index.php?format=123&user=wsscat&age=13
或者
http://127.0.0.1/wsscat/?format=123&user=wsscat&age=13
这个就会得到下图的结果
这里写图片描述

使用CodeIgniter框架搭建RESTful API服务

如何使用PHP的CodeIgniter框架来编写API接口

对比CodeIgniter的REST_Controller和CI_Controller接收参数,输出的异同

chriskacerguis/codeigniter-restserver

awhitney42/codeigniter-restserver-resources示例

使用CodeIgniter 创建 RESTful 服务 REST API

github中CI RESTful的所有例子和项目

Mac小技巧

系统所有隐藏文件显示

系统所有隐藏文件显示,终端下输入以下命令

defaults write com.apple.finder AppleShowAllFiles -bool true

或者用快捷键

command + shift + .

显示文件夹所有的隐藏文件

ls -a

将文件夹拉到终端获取文件或者文件夹路径

查找终端

在finder里面搜索“终端”

查找文件

在finder中按以下三个组合按键,可以查找文件,例如host文件

command + shift + g

IOS模拟器的Home快捷键

command + shift + h

输入法切换

command + 空格

不过现在这个快捷键好像改了,影响不大,不过我是被搜过输入法惯着了,改不回来

管理员权限

输入一下命令会获取管理员权限,在某些情况下要用到管理员权限,输入正确密码才能获取该权限

sudo -i

例如:在mac系统下安装node模块包,要在npm开头加上sudo,用管理员权限安装,不然没有权限去新建文件夹等导致安装失败

Macbook设置热点

Mac做wifi热点

打开根目录文件

用终端输入以下命令

open .    #打开使用者的目录
open /    #打开系统根目录

安装brew

参考Homebrew

在终端输入以下命令,安装brew之后,可以利用它来安装其他常用的包

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

屏幕截图

command + shift + 4

远程连接

试了几个发现Parallels Client比较好用,直接在App Store下载即可

Mamp Pro

Mac 中 mamp pro 的 localhost 显示为 403 Forbidden

Xclient

推荐一个Mac破解软件的网站,上面有大量的工具,新手值得尝试,不过最好还是支持正版,谢谢🙏

精品MAC应用分享

谷歌访问助手

I find a good extension which name is the assistant of accessing Google in order to visit some technology sites. You can search the key word on the Internet in any browsers. After you download it, you can install it in chrome. Through it, many services of Google will be normally used. It's extremely wonderful and useful extension I recommend.

win to go

可以参考这篇文章

说走就走的「Windows」—— Windows To Go 制作详解

有几点要注意的是我在PD虚拟机里面制作的启动盘,移动硬盘要选择用虚拟机读取,而不是mac系统本身,不然win to go无法获取移动硬盘

还有注意win启动盘弄好了之后,要用command+r进入设置几个关键设置,允许读取外盘系统和禁止网络(不然启动的时候会一直要求网络),记得要准备一个U盘再mac系统中下载win驱动(启动转换助理BootCamp),然后烤进去在win系统中安装,因为win启动之后所有驱动都没有的

Postman

5 million developers use Postman. You should too!

Wine

先安装Xquartz,然后再安装Wine

配置Wine的环境路径

test "$?BASH_VERSION" = "0" || eval 'setenv() { export "$1=$2"; }';setenv PATH "/Applications/Wine Stable.app/Contents/Resources/start/bin:/Applications/Wine Stable.app/Contents/Resources/wine/bin:$PATH";

使用的时候

wine xxx.exe

清理全部 node_modules 文件夹

你的硬盘已满,清理 node_modules 来腾出空间

gitkraken

一个很好用的 git 管理工具

https://www.gitkraken.com/

破解版教程: https://github.com/5cr1pt/GitCracken/tree/master/GitCracken

截屏2020-03-26下午3 42 03

Cordova配置(WebApp混合开发环境)&&Hbuilder打包配置

必备环境安装

1.配置JDK和Gradle环境

用的是1.8的版本,网上很多地方可以下载,这里不上链接了
在本地配置sdk变量,如图,点击桌面(计算机)->右键属性->高级系统设置->系统属性面板高级->点击环境变量->在下面框中的系统变量中新建
这里写图片描述

JAVA_HOME          D:\Program Files\Java\jdk1.8.0_112//这个路径根据你安装JDK的环境对应其地址
PATH                      %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;//PATH本来是有内容的,不要全部替换,用分号隔开,再往后继续添加
CLASSPATH            .;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar//注意不要漏了前面的点

//新版本需要配置如下Gradle环境
GRADLE_HOME      D:\Android_Studio\gradle\gradle-2.14.1-all //指向gradle的已解压文件的根目录配置path:
PATH                      %GRADLE_HOME%\bin //继续往PATH后面添加环境变量

2.下载Android SDK

推荐国内的android-studio下载
然后配置Android SDK环境,ANDROID_HOME定位到你的SDK文件夹的根目录

ANDROID_HOME     D://android-sdk-windows     //对应你的SDK文件夹的目录

重启电脑

Cordova安装

1.下载Node.js

node官网

2.在mac终端运行下面命令,输入密码安装cordova

sudo npm install -g cordova

如果是Window系统,去掉sudo

npm install -g cordova

这里写图片描述
sudo是因为需要管理员权限,-g是全局安装
cordvoa官网命令行帮助

3.执行以下代码创建一个cordvoa应用

cordova create hello com.example.hello HelloWorld

第一个参数是文件目录,第二个参数是app id, 第三个参数是显示的title
这里写图片描述

IOS打包

4.定位到hello目录文件夹,为项目安装平台模块

cd hello
cordova platform add ios

这里写图片描述

5.打开xcodeproj项目的文件位置双击打开,如果已安装Xcode就能顺利的打开项目了

hello/platform/ios/
这里写图片描述

6.可以用编写html项目的IDE打开www下的index.html查看效果,在浏览器打开即可

这里写图片描述

7.在Xcode打开iOS 模拟器如果看到以下效果说明环境已经搭建成功

这里写图片描述

ANDROID打包

续前面三步

4.定位到hello目录文件夹,为项目安装平台模块

cd hello
cordova platform add android

5.生成APK文件

cordova build android

image
遇到这个问题就是gradle下载失败了,可以尝试拿图中的链接手动下载然后把它放到对应的系统文件下,如下,注意版本一定要对应上

C:\Users\Administrator\.gradle\wrapper\dists\gradle-2.14.1-all\4cj8p00t3e5ni9e8iofg8ghvk7

image
gradle-2.14.1-all.zip下载地址
或者android-studio下载
成功就会显示如下
image
apk的目录如下:

项目路径\test\platforms\android\build\outputs\apk

6.更改应用桌面图标

在cordova生成项目的跟目录创建res文件夹
image
然后更改config.xml文件

ldpi : 36x36 px
mdpi : 48x48 px
hdpi : 72x72 px
xhdpi : 96x96 px
xxhdpi : 144x144 px
xxxhdpi : 192x192 px

<platform name="android">
        <allow-intent href="market:*" />
        <icon src="res/android/36.png" density="ldpi" />
		<icon src="res/android/48.png" density="mdpi" />
		<icon src="res/android/72.png" density="hdpi" />
		<icon src="res/android/96.png" density="xhdpi" />
		<icon src="res/android/144.png" density="xxhdpi" />
		<icon src="res/android/192.png" density="xxxhdpi" />
    </platform>

7.调用相机

cordova plugin add cordova-plugin-camera

安装成功就会在plugin出现两个文件夹cordova-plugin-cameracordova-plugin-compat
image

CSS图片响应式布局

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Wsscat DEMO</title>
    </head>

    <body>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
    </body>
    <style>
        body {
            margin: 0;
            padding: 0;
        }

        .box {
            float: left;
            width: 30%;
            margin-top: 10px;
            margin-left: 2.5%;
            height: 0;
            padding-bottom: 33.98%;
            background-color: #dbe0e4;
            background-image: url(29381f30e924b899de6cd36f68061d950a7bf611.jpg);
            background-size: cover;
        }
    </style>
</html>

Angular源码解析参考文章

angular源码一,module加载器
http://blog.csdn.net/dagouaofei/article/details/38085357

angularjs源码分析之:angularjs执行流程
https://segmentfault.com/a/1190000002549996

用angularjs遇到的坑们
http://www.cnblogs.com/zhrj000/p/3383898.html

Angular源码理解–启动过程
http://blog.csdn.net/ybdesire/article/details/50719173

angularjs1.3.0源码解析之执行流程
http://www.html-js.com/article/Source-code-analysis-original-the-execution-flow-of-angularjs130-source-code-analysis

AngularJS模块详解
http://blog.csdn.net/woxueliuyun/article/details/50962645
在路由里面实现延迟加载,注意$.getScript要先加载jQuery才能运行

resolve: {
                        delay: function($q) {
                            var delay = $q.defer(),
                                load = function() {
                                    $.getScript('.js', function() {
                                        delay.resolve();
                                    });
                                };
                            load();
                            return delay.promise;
                        }
                    }

angular.fromJson方法与toJson方法
http://blog.csdn.net/itsonglin/article/details/47429867

angularjs源码分析之:angularjs执行流程
http://www.cnblogs.com/Leo_wl/p/3771304.html

Angular showcase!
http://showcase.ngnice.com/#/home/home

node技巧

npm被墙或者太慢可以用阿里的cnpm指令
npm install -g cnpm --registry=https://registry.npm.taobao.org
image

Atom技巧总结

我的常用快捷键

ctrl-alt-b 安装atom-beautify后可使用,格式化代码
ctrl-f 搜索,查找和替换文件
image
ctrl-/ 注释代码
image
ctrl-m 例如函数括号前后之间的切换,或者和之间的切换
image
ctrl-shift-m Markdown预览,非常好用的一个功能,注意会和搜狗输入法的快捷键起冲突
image
ctrl-z 撤销上次的键入
alt-shift-s 查看当前可以使用的代码块,例如我想写一个for循环,IDE就帮我自动生成这个代码块常用的格式
image
image
ctrl-鼠标滚动 放大和缩小代码
image
ctrl + r 进入变量、函数跳转面板
image
ctrl + g 进入行号跳转面板

我常用的Packages

atom-beautify
image
点击install之后,当安装完毕我们就可以用快捷键ctrl-alt-b就可以格式化代码了

emmet
这个扩展相信很多前端都不陌生,其实我个人是不喜欢用这个的,因为要记住一部分指令是比较头痛的事吧,等于多记一部分简写的标签,当然如果你很熟练它的确能给你带来高效的编码习惯,
使用手册可以参考这两篇文章
Emmet Documentation
前端开发必备!Emmet使用手册

minimap
用过sublime对右边这一个竖块视图肯定不会陌生,他显示全部代码的缩小版,能让你快速找到一些关键的代码块
image

ile-icons
这个是一个视觉的插件,本身是没有什么功能,但是看着文件前面有彩色的图片,顿时感觉编程就是一门艺术了
image

git-plus
一个Git的插件,我比较喜欢的是它能够方便的切换分支
image

关于Atom的网站或者文章

Atom China
如何评价 GitHub 发布的文本编辑器 Atom?——知乎
Atom 常用快捷键
Atom编辑器折腾记_(2)基础了解使用

Javascript面向对象编程

JS面向对象的程序设计,JS在很早以前一直被认为毫无章法的语言,大部分初学者写的时候都是运用面向过程的思维来编写JS的(一页下来就是一个function接着另一个function的写法)

其实JS中function就是一个对象,如下面的代码

var HelloWscats = function(){
    console.log("wscats is not cat");
}

var _init = function(){
    var obj = new HelloWscats();
}

_init();//log:wscats is not cat

此时我们就可以调用_init 方法后打印wscats is not cat,它调用了HelloWscats的对象,当然这个HelloWscats对象没有任何属性和方法,它只有一个构造方法HelloWscats(),相当于调用了一个没有任何属性的方法和的类,当使用new进行创建的时候,就调用了它的构造方法(也就是执行了HelloWscats里面的console.log("wscats is not cat");),当然现在很多JS的对象直接是定义了属性而已,而构造方法里面是没有其他可执行的代码的

其实HelloWscats 就是我们所说的创建对象中的其中一种构造函数模式

var _init2 = function(param){
    var obj = new Object();
    var obj = {}
    obj.action = "b";
    Object.defineProperty(obj, "name", {
        configurable: false,
        writable: false,
        value: "Wscats",
    });
    obj.name = "wscats";//无效
    delete obj.name;//无效
    console.log("wscats is not cat");
    return obj;
}
_init2("wscats is not cat");

我们稍微改一下,执行_init2函数的时候,相当于创建对象中的另一种我们说的工厂模式,创建对象交给一个工厂方法(_init2)来实现,可以传递参数,这里创建对象的方式跟构造函数方式的区别在于
构造函数能看到具体的定义对象类型HelloWscats,而工厂模式不能,因为工厂模式创建对象都是使用Object的原生构造函数来完成的,如图
本质是这两句结果的区别

var obj = new HelloWscats();//构造HelloWscats函数,并实例化HelloWscats对象
var obj = new Object();//new一个Object对象,在工厂函数里面扩展该对象,并return该对象

再写一个简单的例子来说明他们的区别,如下

var Constructor = function() {
    console.log("wscats is not cat from constructor");
}

var objFromConstructor = new Constructor();

var Factory = function() {
    var obj = new Object();
    console.log("wscats is not cat from factory");
    return obj;
}

var objFromFactory = Factory();

这里写图片描述

上面就是一个最简单的对象,不过既然是对象,肯定是要赋予它属性和方法的
我们可以用prototype这个原型的关键字进行赋值,比如我们要给HelloWscats对象增加一个name属性和say方法,那我们就可以这样添加

var HelloWscats = function(){
    this.action = "Code";
    console.log("wscats is not cat");
}

HelloWscats.prototype = {
    name: "wscats",
    say: function(){
        console.log("Wscats is not cat");
    }
}

HelloWscats.prototype.skill = "Good Cat";

var _init = function(){
    var obj = new HelloWscats();
    obj.say();
    console.log(obj);
}

_init();

这里我们看到了先是执行了构造函数中的console.log("wscats is not cat");,然后在执行原型链中的console.log("Wscats is not cat");
打开浏览器看看打印的obj对象具体是怎么样的
image

上面有一定注意的是,如果我实例化了HelloWscats对象

var _init = function(){
    var obj = new HelloWscats();
    obj.say();
    obj.action = "b";
    console.log(obj);
}

我们可以改变,这个对象的action属性obj.action = "b";,但我们没办法改变原型链的对象,这个就类似于继承了HelloWscats对象的原型链的对象,继承的这些子类无法更改,但是子类可以修改自己本身的属性和方法

var HelloWscats = function(){
    var privateVvariables = "I am ur cat";
    this.getPrivate = function(){
        console.log(privateVvariables);
    }
    this.action = "Code";
    console.log("wscats is not cat");
}

HelloWscats.prototype = {
    name: "wscats",
    say: function(){
        console.log("Wscats is not cat");
    }
}

HelloWscats.prototype.skill = "Good Cat";

var _init = function(){
    var obj = new HelloWscats();
    obj.say();
    obj.action = "b";
    obj.getPrivate();
    console.log(obj);
}

_init();

下面我可以继续在构造函数里面写一个var privateVvariables = "I am ur cat";来让HelloWscats拥有一个私有成员变量,当然这个变量也只有该对象的方法能访问,具体就是this.getPrivate函数能读到该私有变量的成员

this.getPrivate = function(){
    console.log(privateVvariables);
}

它的巧妙之处其实在于这个构造函数的作用域,只能在类的构造函数里面进行访问该私有变量

var _init = function() {
    var obj = new HelloWscats();
    obj.say();
    obj.action = "b";
    obj.getPrivate();
    Object.defineProperty(obj, "name", {
        configurable: false,
        writable: false,
        value: "Wscats",
    });
    obj.name = "wscats";//无效
    delete obj.name;//无效
    console.log(obj);
}

上面我们一直用obj.name = "wscats"或者obj = {name: "wscats"}这两种方法来编辑或者定义obj的属性,那么有没有其他更好的方法呢,答案是有的,正如上面的这一段代码

Object.defineProperty(obj, "name", {
    configurable: false,
    writable: false,
    value: "Wscats",
});

defineProperty方法能修改对象的属性它接收三个参数:属性所在对象,属性名和一个描述符对象(必须是:configurable、enumberable、writable和value,可设置一个或多个值)
他可以设置以下的数据属性(他们默认都为true),这四个数据属性也是对象属性的默认四个数据属性:

  1. configurable:表示能否使用delete操作符删除从而重新定义,或能否修改为访问器属性。默认为true;
  2. Enumberable: 表示是否可通过for-in循环返回属性。默认true;
  3. Writable: 表示是否可修改属性的值。默认true;
  4. Value: 包含该属性的数据值。读取/写入都是该值

正如我写obj的name属性configurable: false,writable: false,

obj.name = "wscats";//无效
delete obj.name;//无效

删除该对象的name属性值和修改name属性值也变得无效

Angular用ng-repeat生成表单并绑定ng-click时的一个细节

偶然发现一个问题是ng-repeat里面生成的DOM并绑定ng-click的时候,时间cardshow写法是正确可运行的,但是一旦写成cardShow就会报错,这点试错了很久才发现,记下来防止后人踩坑,看到的就留意一下这个地方,避免困扰了,貌似驼峰写法是不支持的,不知道是否姿势不对哈哈~

$scope.cardshow = function(e){
    console.log(e);
    console.log("测试一下");

}
$scope.cardShow = function(e){
    console.log(e);
    console.log("测试一下");

}
<table class="table table-striped" style="font-weight:normal;">
                        <tr value='{{cardShows}}' class="{{cardShows.length==0&&cardShows?'ng-hide':'ng-show'}}">
                            <th style="font-weight:normal;">名字</th>
                            <th style="font-weight:normal;">价格</th>
                            <th style="font-weight:normal;">操作</th>
                        </tr>
                        <tr ng-repeat="cardShow in cardShows">
                            <!--<th>{{cardShow.card_id}}</th>-->
                            <td style="font-weight:normal;">{{cardShow.card_name}}</td>
                            <td style="font-weight:normal;">¥{{cardShow.price}}</td>
                            <td style="font-weight:normal;"><div style="margin: 0; padding: 0;" class="btn btn-primary" type="button" value="删除" ng-click="cardshow(cardShow)">删除</div></td>
                        </tr>
                    </table>
    </div>

Vim笔记

Vim笔记

Vim是个很好的编辑器,远古神器嘛,当你熟悉了这个编辑器你的逼格是不是瞬间就会高了许多
首先安装vim
当然学习一个编辑器都是为了方便自己,提高效率,可是vim这个文本编辑器的学习曲线是陡峭的
但苦练会让你带来搞效率
而我自己在用git命令的时候再配合vim确实是很强大的开发组合,虽然vim没有代码提示,但是它本身其实是有很多的插件供使用的,当我们git提交代码的时候,用vim来对代码进行解决冲突和编辑都是非常方便的
VIM下载
vim
启动vim

image

i
Insert模式,按 ESC 回到Normal模式
反正如果不知道现在是在什么模式下,就一直按ESC,回到Normal模式,下面功能键都是在Normal模式触发的

image

此时Insert模式跟记事本一样

image

操作vim会经常在这两个模式下切换,就如同在记事本中你在选项卡和编辑内容窗口中切换一样

image
image

只不过这里大部分变成了键盘操作
所以在Normal模式下,键盘上所有的键都变成对应的功能键了,就如同我们按c跟按ctrl+c一样,c在后面就变成了对应的功能键了

x
Normal模式下,按x删除当前光标所在的一个字符

image

其实这个相当于Insert模式下的退格键

:wq
保存+退出vim(:w 保存 :q 退出) 后面跟文件名

image

这个相当于记事本中的文件->(保存)另存为

dd
删除当前行,并把删除的行保存到剪贴板里面,也是IDE里面常用的,快速删除一行代码,不用长按退格了

p
粘贴剪切板,这个功能挺常用的,如果在外面复制完东西,直接在vim界面按p就可以粘贴了

yy
复制当前行到剪切板

da811428-28e3-11e6-92a6-cf371a09e4e9

help
显示相关命令的帮助

image

:q
退出
:q!
不保存直接退出

image

a
在光标后插入,如果本来光标不在最后就会自动帮你定格到最后输入

image

o
在当前行后面插入一个新行,相当于记事本一行的最后敲回车键

image

0
按数字0就是到行头,跟a是相反的

image

^
这个是要结合shift+6,到本行第一个非blank字符的位置(所谓blank字符就是空格,tab,换行,回车等)

$
光标切换到本行行尾

/pattern
搜索 pattern 的字符串

如果搜索出多个匹配,按n可以定位到下一个

u
撤销 就是Undo
这个功能基本IDE都是有的,挺常用的,如同我用PS时候ctrl+z那样方便

ctrl+r
重做 也就是Redo

:e <文件路径/文件名>
打开一个文件,这个在cmd中我常常cd到指定目录,然后dir显示再用vim来打开相应的文件来进行编辑

image

:w
保存

image

保存后会出现written的提示

image

:saveas <文件路径/文件名>
另存为

image

保存成功后也会出现written的提示

image

有一点需要注意的是如果只是保存或者另存为但是一直没有退出vim编辑的话,文件会一直以下图后缀在文件夹

image

但是如果保存并退出了,就会变成保存时候定义的文件名和后缀了

image

:x
ZZshift+z
:wq
上面这三个都是保存并退出,注意ZZ是不需要输入冒号和回车的
image

:q!
退出但不保存
:qa
强行退出所有的正在编辑的文件,就算别的文件有更改

:bn
:bq
有时候我们需要打开多个文件,就是在一个文件已经打开的情况下我们可以继续用:e来打开其他文件,当打开多个文件的时候,可以使用上面这两个命令来切换下一个或者上一个文件

image

.
这个命令可以重复执行自己运行的上一个命令

N<命令>
打这个的时候是不可见的,也就是盲打
N就是输入一个数字,比如我输入9p,就会出现下图这个

image

G
将光标定位到最后一行的头部

image

w
到下一个单词的开头。
e
到下一个单词的结尾。

  • 如果你认为单词是由默认方式,那么就用小写的e和w。默认上来说,一个单词由字母,数字和下划线组成
  • 如果你认为单词是由blank字符分隔符,那么你需要使用大写的E和W。

*和#
当你把光标放到catwsscat上的时候按*或者#就会移动到匹配该单词的上一个或者下一个

  • *在键盘左边是匹配上一个单词
  • #在键盘右边是匹配下一个单词

image

%
匹配括号移动,支持(,{, [,你需要把光标先移到括号上

image

v
进入可视化选择后按键盘的上下左右的方向键都会帮你自动选择

qq 20160603100040

可视化选择下
J → 把所有的行连接起来(变成一行)
< 或 > → 左右缩进
= → 自动给缩进 (这个确实挺方便的)

Angular自定义判断上一页是否存在的服务

当我们从一个页面跳进一个新页面的时候,有时候我们会需要判断是否存在上一个页面
在Javascript中有一个方法原生方法可以实现
document.referrer
定义和用法:referrer 属性可返回载入当前文档的文档的 URL。
w3school是这样说明它的:如果当前文档不是通过超级链接访问的,则为 null。这个属性允许客户端 JavaScript 访问 HTTP 引用头部。

<html>
<body>
The referrer of this document is:
<script type="text/javascript">
document.write(document.referrer)
</script>
</body>
</html>

由于这个是比较常用的方法,我把它写成一个Angular的服务来方便调用,在用到的地方再注入referrer这个服务就可以实现对应的方法
写的很简单,具体如下:

<!DOCTYPE html>
<html ng-app="wsscat" ng-controller="serCtrl">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0,user-scalable=no">
        <title></title>
        <script type="text/javascript" src="ng.js"></script>
    </head>
    <body>
    </body>
    <script>
        var app = angular.module('wsscat', []);
        app.controller('serCtrl', function($scope, cat, referrer) {
            referrer.log();
        })
        app.service('referrer', function() {
            var obj = {
                url: function() {
                    var url = document.referrer ? document.referrer : '';
                    return url;
                },
                back: function() {
                    window.history.go(-1);
                },
                //判断是否有上一页,如果有则返回,没有则在
                is: function() {
                    this.url ? this.back() : '';
                },
                log: function() {
                    console.log('当前页为:' + window.location.href);
                    console.log('上一页为:' + this.url());
                }
            }
            return obj;
        });
    </script>
</html>

这里把常用的返回刷新列出来,实际情况可以根据需求用相对应的替换掉服务中的window.location.href

  • history.back(-1):直接返回当前页的上一页,数据全部消息,是个新页面
  • history.go(-1):也是返回当前页的上一页,不过表单里的数据全部还在
  • history.back(0) 刷新 history.back(1) 前进 history.back(-1) 后退
  • window.location.reload();//刷新
  • window.history.go(1);//前进
  • window.history.go(-1);//返回+刷新
  • window.history.forward();//前进
  • window.history.back();//返回

需要注意的是必须从一个页面跳到这个例子的页面才会触发document.referrer,刷新页面等referrer都会返回空,路由切换视图的跳转的也是不成功的,原因在于视图切换其实本质上是异步的刷新页面

如果要返回上一个路由,我觉得可以从routeChangeSuccess下手

$rootScope.$on('$routeChangeSuccess',
                    function() {
$rootScope.loginBack = window.location.href;
})

可以把当前的页面记录下来保存到一个全局的loginBack中,然后根据情况再去返回对应的路由
还有一个在Angular里面挺好用的跟原生一样的函数
$window.history.length
length 属性声明了浏览器历史列表中的元素数量
有什么地方可以用到了,如果你是刚进来这个页面则返回的是1
如果你是路由中切换了几次那么这个值肯定会大于1
所以我们可以用这个方法来判断我们是否首次加载进入这个Angular框架
甚至我们还可以用它来监听用户切换过多少次路由,来获取相应的信息

关于Angular服务Request异步请求的详细分析

首先这里我简单写个例子来方便您的理解

var request = {
    post: function() {
        var errorCallback = {
            error: function(f) {
                this.fun = f;
            },
            fun: function(data) {}
        };
        successCallback = {
            success: function(f) {
                return this.fun = f, console.log(this.fun), errorCallback;
            },
            fun: function(data) {
                console.log(data)
            }
        };
        returnData = {
            wsscat: "hello"
        };
        //成功
        //var a = successCallback.fun(returnData);
        a = successCallback.fun;
        setTimeout('a(returnData)', 1000)
        return successCallback;
        }
        }
        request.post().success(function(data) {
            console.log("123");
            console.log(data);
        })
        console.log("wscat's test");

这里首先要理解一点很重要的就是异步回调,Javascript的异步回调其实就两个常用方法,
setTimeout定时这类和ajax请求这类
上面代码跟angular的request服务类似,首先是这个request对象,就相当于angular注入request服务之后返回的request对象
这个对象写了一个post方法,而post方法里面这有两个回调对象分别是
errorCallback和successCallback
还有一个模拟ajax请求返回的returnData对象

returnData = {
            wsscat: "hello"
        };

这个在angular里面则是ajax请求服务器返回给你的json对象
我们看看这段代码执行之后会log出什么的信息,如下图
image
如果熟悉Javascript的话这里不难理解,首先是输出的函数信息是因为success()函数里面的console.log(this.fun)这句
request.post().success()
success()里面首先this.fun = f这句是,把匿名的回调函数赋给successCallback对象里面的fun方法,保存起来让后面setTimeout进行回调
所以我们可以看到首先执行出来的是打印出来的回调函数,然后就是代码最后一句的
console.log("wscat's test");
明白了上述这个流程之后就可以很好的理解angular封装的request服务这一块
只不过angular里面的回调不是setTimeout回调,而是换成了$http回调而已

$http.get(url).success(function(data, status, headers, config) {
//code
})

而$http请求又因为根据返回的成功或者失败把数据
用相应的方法带到我们放到了被匿名回调函数覆盖,对象successCallback的fun里面
而带数据到fun里面就是下面这两句
successCallback.fun(returnData);
errorCallback.fun(returnData);
后面我再深入说一下

$http.get(url).success(function(data, status, headers, config) {
//code
})
  • data:这个不用再多叙述了,我们上面做了这么多其实就是想拿服务器返回给我们的data
  • status:http响应状态码,这个是很基础的东西,但我还是简单说说
  • 200:不用怀疑就是请求成功的意思
  • 304:就是你请求已经被允许的情况下,服务器发现你需要的东西还是跟之前一样没变,则返回给你304
  • 404:请求失败了,请求的资源再服务器没有发现
  • 500:看到5一般就是服务器出错了
  • 常看到的就这几个吧
  • header:头信息
  • config:生成原始请求的设置对象,如下图
    image

再往下看,其实post请求和get请求区别是不大的
只是这里get更简单明了,你直接传url过来就行了,相比post请求接口还要带上各种需要请求的参数
但再仔细了解的话,其实post跟get在这里其实都走get请求
image

妙用Javascript运算符“||”和“&&”

var a = 1;
        var b = 2;
        if (a < b && a == 1) {
            //console.log("&&");
        }
        //上面改写
        a < b && a == 1 && console.log("&&");
        if (a > b || a == 1) {
            //console.log("||");
        }
        //
        a > b || a == 1 && console.log("||");
        //&&前面为真运行后面 否则不运行
        var c = (a == 1) && 3
        var d = (a == 2) && 3
        console.log(c); //3     取了后面的3
        console.log(d); //false 取了前面的false
        //||前面为假运行后面 否则不运行
        var c = (a == 1) || 3
        var d = (a == 2) || 3
        console.log(c); //true
        console.log(d); //3
        //巧用
        var e = 0;
        var f;
        switch (f) {
            case 5:
                e = 1;
                break;
            case 10:
                e = 2;
                break;
            case 12:
                e = 3;
                break;
            case 15:
                e = 4;
                break;
            default:
                e = 0;
                break;
        }
        var e = (f == 5 && 1) || (f == 10 && 2) || (f == 12 && 3) || (f == 15 && 4) || 0;
        console.log(e); //0

总结理解为
&&遇上假就停止,并返回这个假,相反遇到真就继续执行直到拿最后一个
||遇上真就停止,并返回这个真,相反遇到假就继续执行直到拿最后一个
这样会不会好理解多
注意:非0的整数都为true,undefined、null和空字符串”" 为false。

Angular的ng-style用法

DEMO的效果如下
image
ng-style属性值需要传入对象
对象由CSS属性和值注册,即key=>value键值对
对象的格式如同DEMO中的这个对象

{
                "color": "#FFF",
                "background-color": "#FF7E4F",
                "font-size": "60px",
                "padding": "20px"
            }

全部的代码如下

<!DOCTYPE html>
<html ng-app="wsscat" ng-controller="catCtrl">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0,user-scalable=no">
        <title></title>
        <script type="text/javascript" src="ng.js"></script>
    </head>

    <body>
        <div ng-style="myStyle">Wsscat</div>
    </body>
    <script>
        var app = angular.module("wsscat", []);
        app.controller("catCtrl", function($scope) {
            $scope.myStyle = {
                "color": "#FFF",
                "background-color": "#FF7E4F",
                "font-size": "60px",
                "padding": "20px"
            }
        });
    </script>
</html>

有时候我们需要在DOM中直接把对象写在ng-style里面,里面还要绑定一些$scope的数据
那么我们可以这样,注意对象大括号里面用单引号,用双引号是无法运行的

<div ng-style="{
            'color': '#FFF',
            'background-color': '#FF7E4F',
            'font-size': '60px',
            'padding': '20px'
        }">Wsscat</div>

然后我们再复杂一点,绑定$scope给视图的数据,我们就可以再这样写

<div ng-style="{
            'color': '#FFF',
            'background-color': '#FF7E4F',
            'font-size': '60px',
            'padding': '{{padding}}'
        }">Wsscat</div>

在控制器定义好padding就可以了,这种情况在某些情况是会运用到的
$scope.padding = "30px";

百度设置小度机器人出现

很久之前写的一篇小干货,在浏览器中打开百度搜索
最近没测试这个方法了,希望还能用
https://www.baidu.com/
image
找到右上角设置按钮,点开吧~~
选择搜索设置,出现如图,我用的是firefox噢,不过浏览器不重要滴,大家可以用chrome或者其他都行
image
如果呢正常登陆了百度账号滴,这里就只有8个选项,没登录账号也是8个选项噢,当然有部分人可能不是滴在最后一个选项里,咦下面
通栏浏览模式:是否希望点击右侧推荐内容时打开通栏浏览模式 打开 关闭这里选择右击->查看元素
image
确定操作无误之后呢,会出现下面界面,不同浏览器出现的效果可能跟小题主的不同,大部分是在右边出现的
image
看到红色框的那个就是我们要操作的目标啦,它的id为xiaoduConf,看到,这个就正确了
image
点开之后出现以下DOM结构,在style中双
image
然后成功把display:none给删掉就可以了,这个样式的意思就是隐藏了这个所在标签,详情涉及前端H和CSS方面知识,姐就不详细解释了
往下
此时往右看看吧
image
刚才的搜索设置里面就会多出现了一个,从八个选项变成有九个设置选项~
小度机器人:是否希望在搜索结果页开启小度机器人
默认按钮应该都是开启的,如果不是开启的话,选择开启吧,然后点下面的保存设置
好啦到这里我们就完成了第一步啦但是这个小度机器人开启在那里呢,别急
我们在搜索框里面输入一个-> 2016高考 然后百度一下
image
现在你就看到这个调皮的机器人躲在这个边际线啦
好吧赶快点击它出来吧
image
此时的小度在提醒充电,(哼姐我弄了这么久你提醒我你开不了机
跟我来看到聊天框左下角的激活小度没,点开你吧
image
然后输入昵称和我主人昵称再就可以了啦
image
现在测试下它啦,输入问它一些问题,它就会回答你的~~~
我淘气的输入了我是尤美三次,它居然回答了我三个不同的结果,好吧,姐欣赏你,不错不错,蛮可爱的,像我(就别自恋了)!
其他黑暗功能大家自行去找咯,就不在这里一一写了
转载的请告知我一声
闪啦

Angular源码解读publishExternalAPI函数

function publishExternalAPI(angular){}
publishExternalAPI就是将一些通用的方法挂载到全局变量angular上,然后我们就可以以API的形式进行调用
函数首先传入一个angular的全局变量,下面将会遇到在前面定义的好的一个extend函数

function extend(dst) {
    var h = dst.$$hashKey;
    return forEach(arguments, function(obj) {
        obj !== dst && forEach(obj, function(value, key) {
            dst[key] = value
        })
    }), setHashKey(dst, h), dst
}

这里的方法就是将某些通用的方法挂到一个对象上面,extend传入和返回都是dst形参对象,里面经历了两个forEach,forEach也是angular前面定义好的函数,extend根据传入的arguments个数进行判断,dst是需要挂载的对象,arguments中除了dst外都是需要被挂载到dst中的对象
obj !== dst && forEach(obj, function(value, key) {})
上面这句就是将arguments中的dst对象排除出去,然后对dst进行对象挂载
然后就转化成

angular['boostrap'] = boostrap;
angular['copy'] = copy;
angular['extend'] = extend;
//...省略若干个通用的方法

上面就不一一列出所有方法,当然当我们对加载了angular.js的页面进入控制台进行打印就会很清晰的看出这里的全部方法
console.log(angular);
如图所示
image
这些通用的工具函数在实际的项目中会经常使用到,挺方便的例如forEach,toJson和fromJson这些常用函数
当然就如上面的extend也可以是做API调用,也写个简单的例子,同样在控制器里面输入一段代码,然后打印,这里创建一个空对象,并把该对象后面的对象拷贝并挂载到该空对象中,并赋值给wscat,那么wscat对象就有对应的这些属性
var wscat = angular.extend({}, {name:"wscats",skill:"none"})
console.log(wscat)
image

咱们继续往下看
angularModule = setupModuleLoader(window)
这里为angular对象创建和加载module()函数,这里我在Angular源码解读setupModuleLoader函数有详细的分析,这里就不多说了,有需要的的可以回头看一下
继续下面这句

try {
    angularModule("ngLocale")
} catch (e) {
    angularModule("ngLocale", []).provider("$locale", $LocaleProvider)
}

try代码块里面首先判断是否获取到获取ngLocale模块,如果我们获取不到,则就报个异常(ngLocale默认没有创建的)
catch代码块接受上面的异常(也就是获取不到ngLocale模块走catch分支),然后在这个位置注册ngLocale模块

简单的说这里angular.module在创建模块的时候,传递一个参数的时候,是获取模块;传递一个以上的是创建新模块;所以try里面是获取,catch里面就是创建了
那么这里就成功的创建了一个ngLocale模块,其实angularModule实际上就是angular.module
angularModule = angular.module
angularModule("ngLocale", []) = angular.module("ngLocale", [])
所以我们就可以链式去调用对象moduleInstance中的directive和provider

JS日期对比

今天刚好用到这个函数,写了一个日期对比的例子

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>写一个JS对比时间的例子</title>
    </head>

    <body>
    </body>
    <script>
        function compareTime(a, b) {
            var arr = a.split("-"); //log [2016,04,06]
            var start = new Date(arr[0], (arr[1] - 1), arr[2]);
            var starts = start.getTime(); //输出时间戳进行对比
            var arrs = b.split("-");
            var end = new Date(arrs[0], (arrs[1] - 1), arrs[2]);
            var ends = end.getTime();
            if (starts >= ends)
                console.log('开始时间大于结束时间');
            else
                console.log('开始时间小于结束时间');
        }
        compareTime('2016-05-03', '2016-05-04');
    </script>

</html>

有一点要注意的就是这一句
var starttime = new Date(arr[0], (arr[1] - 1), arr[2]);
月份记得在函数里面减去1
因为在Javascript中,月份的数值是从0到11之间的整数(1月至12月),例如5就是代表6月,
这里要是要注意的
其他的话按照正常转换为时间戳进行对比即可

后面再增加一个方法就是判断时间是星期几,看改进的例子

function compareTime(a, b) {
            var arr = a.split("-"); //log [2016,04,06]
            var start = new Date(arr[0], (arr[1] - 1), arr[2]);
            var starts = start.getTime(); //输出时间戳进行对比
            var arrs = b.split("-");
            var end = new Date(arrs[0], (arrs[1] - 1), arrs[2]);
            var ends = end.getTime();
            var weekDay = ["星期天", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];
            //getDay() 方法可返回表示星期的某一天的数字。
            console.log("开始时间是" + weekDay[start.getDay()] + "\r\n" + "结束时间是" + weekDay[end.getDay()]);
            var now = new Date();
            console.log("现在是" + weekDay[now.getDay()]);
            if (starts >= ends)
                console.log('开始时间大于结束时间');
            else
                console.log('开始时间小于结束时间');
        }
        compareTime('2016-05-03', '2016-05-04');

其实关键就是getDay()函数返回值,返回值是0(周日)到6(周六)之间的一个整数。

Angular判断在那个浏览器下打开的服务

.service('OrderFrom', [
    function() {
        var OrderFrom = {
            browser: {
                versions: function() {
                    var u = navigator.userAgent,
                        app = navigator.appVersion;
                    return { //移动终端浏览器版本信息
                        trident: u.indexOf('Trident') > -1, //IE内核
                        presto: u.indexOf('Presto') > -1, //opera内核
                        webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
                        gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核
                        mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
                        ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
                        android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或uc浏览器
                        iPhone: u.indexOf('iPhone') > -1, //是否为iPhone或者QQHD浏览器
                        iPad: u.indexOf('iPad') > -1, //是否iPad
                        webApp: u.indexOf('Safari') == -1 //是否web应该程序,没有头部与底部
                    };
                }(),
                language: (navigator.browserLanguage || navigator.language).toLowerCase()
            },
            from: function(e) {
                if (this.browser.versions.mobile) { //判断是否是移动设备打开。browser代码在上面面
                    var ua = navigator.userAgent.toLowerCase(); //获取判断用的对象
                    if (ua.match(/MicroMessenger/i) == "micromessenger") {
                        //在微信中打开
                        return e?this.debug("weixin"):"weixin";
                    }
                    if (ua.match(/WeiBo/i) == "weibo") {
                        //在新浪微博客户端打开
                        return e?this.debug("weibo"):"weibo";
                    }
                    if (ua.match(/QQ/i) == "qq") {
                        //在QQ空间打开
                        return e?this.debug("Qzone"):"Qzone";
                    }
                    if (this.browser.versions.ios) {
                        //是否在IOS浏览器打开
                        return e?this.debug("ios"):"ios";
                    }
                    if (this.browser.versions.android) {
                        //是否在安卓浏览器打开
                        return e?this.debug("android"):"android";
                    }else{
                        return e?this.debug("browser"):"browser";
                    }
                } else {
                    //否则就是PC浏览器打开
                    return e?this.debug("PC"):"PC";
                }
            },
            debug: function(e){
                alert(e);
            }
        }
        return OrderFrom;
    }
])

在Angular里面写上以上的服务代码就可以在控制器中注入这个服务,以达到获取访问该单页面应用时所在的浏览器信息

angular.module('wscatApp').controller('exampleCtrl', ['$rootScope', '$scope','OrderFrom',
    function($rootScope, $scope, OrderFrom) {
    console.log(OrderFrom.from("debug"));
}])

如上面代码的示例一样,我们可以获取OrderFrom对象,得到以下信息
image
如图我们可以从versions里面得知我们是否Android或者IOS系统,是否移动端,是否iPhone等等的信息
console.log(OrderFrom.from("debug"));
当我们去调用它的时候,可以往里面传debug参数,方便我们在移动端调试的时候alert出必要的信息
image
当然我们也可以在PC端的控制台里面获取我们需要的结果

Git操作

git log
查看我们修改过的历史记录
image

git reset --hard commit id
这是回退到某个版本,commit id号可以只是截取开始的一部分
image

dir
查看文件夹目录里面的所有文件

cat
打印某个文件
image

git reflog
查看记录你执行过的每一次命令
image

git checkout
切换到某个分支
image

git status
push前看看修改过了什么文件
image

git branch -d xxxx
删除本地分支,注意在当前这个分支不能删除自己,只能切换到其他分支再删除当前的这个分支
image

git branch -a
查看本地和远程分支
image

git merge xxx
合并xxx分支到当前分支
image

git add [<file>]
添加文件到缓存区,也可以 "git add ." 添加所有文件到缓存
image

git diff [<file>]
比较当前文件和暂存区文件差异
image

git checkout [<file>]
回滚指定文件
image

Angular自定义service服务详解

在这一部分我想详细的叙述service服务在Angular中的应用
其实Angular自带很多的服务,可能你会好奇的问我服务是指什么,其实服务就是函数或者对象
根据官方的文档,Angular内部自建了超过30个服务
包括常用的$http和$location这两个服务
$http服务常用于给服务器发送请求,并响应服务器传过来的数据(其实就是提供了访问底层浏览器的XMLHttpRequest对象的方法)

var app = angular.module('wsscat', []);
app.controller('httpCtrl', function($scope, $http) {
    $http.get("wsscat.html").then(function (response) {
        $scope.data = response.data;
    });
});

$timeout就是触发我们熟悉原生Javascript的window.setTimeout函数

app.controller('timeoutCtrl', function($scope, $timeout) {
            $timeout(function() {
                $scope.content = "Angular之service详解";
            }, 1000);
        })

$interval相应也就是触发我们熟悉原生Javascript的window.interval函数

app.controller('intervalCtrl', function($scope, $interval) {
            $scope.time = new Date().toLocaleTimeString();
            $interval(function() {
                $scope.time = new Date().toLocaleTimeString();
            }, 1000);
        })

上面这三个就是我们Angular常用到的服务,只要在使用的时候注入到需要此服务中的controller中就可以获取相应的服务方法
但是我们这里是要讲的是自定义service服务,所以我们重点来说说当我们需要自己建立一个服务时候,我们该怎么做
写个简单的例子

var app = angular.module('wsscat', []);
        app.controller('serviceCtrl', function($scope, hexafy) {
            $scope.test = "Angular之service";
            $scope.hex = hexafy.myFunc(255);
        })
        app.service('hexafy', function() {
            this.myFunc = function(x) {
                return x.toString(16);
            }
        });

这里是使用自定义的的服务hexafy将一个数字转换为16进制数
这个对象是在创建应用实例的时候创建的(记住,这个对象是单例对象)
也就是说我们第一次使用这个service时候才会实例化,以后再使用的时候返回的都是同一个对象

前端冷知识,妙用浏览器地址栏

javascript:会使浏览器触发js解释器

javascript:alert('I am wscats :)');

将以上代码贴到浏览器地址栏回车后alert正常执行,会出现一个弹窗。
注意:某些浏览器的javascript: 要自行手打到浏览器地址栏,直接复制粘贴的话IE和Chrome会自动去掉代码开头的javascript:的
可以拿来四则运算balabala~

javascript:alert(3+4-6);

看看圆周率等等~

javascript:alert(355/113);void(0);

深入一点,在页面上一些点击的链接可以利用这种方法实行跳转之后运行js代码,进而进行xss或者钓鱼,这里省略~

<a href="javascript:alert('I am wscats :)');">
I am wscats :)
</a>

当然也可以用来检测是否钓鱼网站,根据提供的URL和网页本身的URL进行对比验证

javascript:alert("本网址域名为:" + location.protocol + "//" + location.hostname + "/" + "\n此时浏览的地址为:" + location.href + "\n" + "\n注意:如果域名对不上,就赶紧关掉噢");

还有一个跟这个类似的用法就是mailto

<a href="mailto:[email protected]">I am wscats :)</a>

链接是一种html链接,能够设置你电脑中邮件的默认发送信息。但是需要你电脑中安装默认的E-mail软件,类似Microsoft Outlook等等。

data:text/html, <textarea style="font-size: 1.5em; width: 100%; height: 100%;" autofocus />

将以上代码输入到浏览器将会出现一个textarea框

当然你也可以更改更复杂一点,比如下面复制粘贴这两个代码到地址栏试试

data:text/html,<button onClick="SaveTextArea()">Save</button> <script language="javascript" type="text/javascript"> function SaveTextArea() { window.location = "data:application/octet-stream," + escape(txtBody.value); } </script> <textarea id="txtBody" style="font-size: 1.5em; width: 100%; height: 100%; boarder: none; outline: none" autofocus> </textarea>
data:text/html, <style type="text/css">#e{position:absolute;top:0;right:0;bottom:0;left:0;}</style><div id="e"></div><script src="" type="text/javascript" charset="utf-8"></script><script>var e=ace.edit("e");e.setTheme("ace/theme/monokai");e.getSession().setMode("ace/mode/ruby");</script>

其他的用法如下:

  1. data:,<文本数据>
  2. data:text/plain,<文本数据>
  3. data:text/html,<HTML代码>
  4. data:text/html;base64,<base64编码的HTML代码>
  5. data:text/css,<CSS代码>
  6. data:text/css;base64,<base64编码的CSS代码>
  7. data:text/javascript,<Javascript代码>
  8. data:text/javascript;base64,<base64编码的Javascript代码>
  9. data:image/gif;base64,base64编码的gif图片数据
  10. data:image/png;base64,base64编码的png图片数据
  11. data:image/jpeg;base64,base64编码的jpeg图片数据
  12. data:image/x-icon;base64,base64编码的icon图片数据

就不一一介绍了

Javascript的setTimeout详细用例

先简单写一个用setTimeoutclearInterval配合写的例子

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <button id="getCodes" onclick="getCodes()">获取验证码</button>
        <button id="reciprocal" style="display: none;">倒计时</button>
    </body>
    <script>
        function getCodes() {
            document.getElementById('reciprocal').style.display = 'block';
            document.getElementById('getCodes').style.display = 'none';
            var time = 60;
            var reciprocal = window.setInterval(function() {
                console.log(time);
                if (time > 0) {
                    document.getElementById('reciprocal').innerHTML = time;
                    time--;
                } else {
                    document.getElementById('getCodes').style.display = 'block';
                    document.getElementById('reciprocal').style.display = 'none';
                    window.clearInterval(reciprocal);
                }
            }, 1000);
            document.getElementById('reciprocal').innerHTML = '获取成功';
            alert('获取成功');
        }
    </script>
</html>

setInterval传递了一个匿名的函数,当然这个匿名函数可以把它写出来例如这样:

var reciprocal = window.setInterval(setI, 1000);
function setI() {
    console.log(time);
    if (time > 0) {
        document.getElementById('reciprocal').innerHTML = time;
        time--;
    } else {
        document.getElementById('getCodes').style.display = 'block';
        document.getElementById('reciprocal').style.display = 'none';
        window.clearInterval(reciprocal);
    }
}

这样同样是可行的,注意是写函数名就可以了

下面我主要讲讲一些细节我们把上面的代码改造成这样

function getCodes() {
    this.name = "Wscats";
    this.num = 223;
}
getCodes.prototype.back = function() {
    console.log(this);
    document.getElementById('reciprocal').style.display = 'block';
    document.getElementById('getCodes').style.display = 'none';
    var time = 60;
    var reciprocal = window.setInterval(function() {
        console.log(this);
        //console.log(time);
        if (time > 0) {
            document.getElementById('reciprocal').innerHTML = time;
            time--;
        } else {
            document.getElementById('getCodes').style.display = 'block';
            document.getElementById('reciprocal').style.display = 'none';
            window.clearInterval(reciprocal);
        }
    }, 1000);
    document.getElementById('reciprocal').innerHTML = '获取成功';
    console.log('获取成功');
}
var gd = new getCodes();
gd.back();

注意下输出的第一个this和第二个this有什么不同,原因在于window.setInterval的对象是window所以在setInterval里面this是指向window的,而back是指向getCodes这个对象的
image
为了让window.setInterval里面的this指向getCodes我们可以更改成这样
用self把this带进setInterval,这样里面的self就是指向getCodes了
var self = this;

var self = this;
var reciprocal = window.setInterval(function() {
    console.log(self);
    //console.log(time);
    if (time > 0) {
        document.getElementById('reciprocal').innerHTML = time;
        time--;
    } else {
        document.getElementById('getCodes').style.display = 'block';
        document.getElementById('reciprocal').style.display = 'none';
        window.clearInterval(reciprocal);
    }
}, 1000);

我们再往下实验,把setInterval函数里面的匿名函数放出来定义

function reciprocal() {
    console.log(this);
    //console.log(time);
    if (time > 0) {
        document.getElementById('reciprocal').innerHTML = time;
        time--;
    } else {
        document.getElementById('getCodes').style.display = 'block';
        document.getElementById('reciprocal').style.display = 'none';
        window.clearInterval(reciprocal);
    }
}

然后实验下面每一个尝试去理解
var reciprocal = window.setInterval(reciprocal, 1000);正常
var reciprocal = window.setInterval(this.reciprocal, 1000);this指向window对象
var reciprocal = window.setInterval("reciprocal()", 1000); reciprocal is not a function,其实也就是window.count()
var reciprocal = window.setInterval(self.back(), 1000); 当前实例的back

深入理解Javascript函数编程

初阶部分
字符串可以保存为变量,函数说他也可以

var wscats = 'wscats';
var wscats_fn = function () {
    return 'wscats';
};

字符串可以保存对象字段,函数说他也可以

var wscats = {
    wscats: 'wscats',
    wscats_fn: function () {
        return 'wscats'
    }
};

字符串可以用时再创建,函数说他也可以

'Ws' + (function() {
    return 'Cat'
})();

字符串可以作为参数传给函数,函数说他也可以

var wscats;
function wscats_fn() {};
function hellloWorld(wscats, wscats_fn) {
    return;
}

字符串可以作为函数返回值,函数说他也可以

return 'wscats';
return function () {
    return 'wscats';
};

所以函数真的是JS里面的上等好公民啊可以穿梭于任何地方,并谈笑风生
高阶部分:
有了上面的例子,那么我们就可以把函数这个小孩子带到好多好玩的地方,往下慢慢看~
首先看第一个例子

var
    obj1 = {
        value: 'eno'
    },
    obj2 = {
        value: 'wscats'
    },
    obj3 = {
        value: 'yao'
    };
var values = [];

function add(obj) {
    values.push(obj.value);
}
add(obj1);
add(obj2);
console.log(values); // eno,wscats

这种写法,大家都知道了,变量会污染全局环境,并且一旦有一个地方忘记修改了就会变得很不稳定,不像函数体一样,只负责内部的输入输出,这里如果融入上下文的其他函数和变量就会变得非常混乱

根据这样修改成第二个例子:

var
    obj1 = {
        value: 'eno'
    },
    obj2 = {
        value: 'wscats'
    },
    obj3 = {
        value: 'yao'
    };

function add(obj) {
    var values = [];
    values.push(obj.value);
    return values;
}
跟下面一样
/*function add(obj) {
    var values = [];
    var accumulate = function() {
        values.push(obj.value);
    };
    accumulate();
    return values;
}*/
add(obj1);
add(obj2);
console.log(add(obj1)); // eno
console.log(add(obj2)); // wscats

这样我们把values数组声明放进去函数内部,这样就不会被其他外部变量*扰了,但是问题又来了,只有最后传入对象值才可以返回,那不是我们的初衷

最后看消除所有尴尬的第三个例子:

var
    obj1 = {
        value: 'eno'
    },
    obj2 = {
        value: 'wscats'
    },
    obj3 = {
        value: 'yao'
    };
var
    Add = function (obj) {
        var
            values = [];
        var
            _calc = function (obj) {
                if (obj) {
                    values.push(obj.value);
                    return values;
                } else {
                    return values;
                }
            };
        return _calc;
        //可以这样把它看成匿名函数省去一个没必要的变量
        /*return function(obj) {
            if (obj) {
                values.push(obj.value);
                return values;
            } else {
                return values;
            }
        }*/
    };
var
    calc = Add();
calc(obj1); //相当于ValueAccumulator()(obj1)
calc(obj2); //走if分支
console.log(calc()); //走else的分支

这里制造一个闭包来保存第一层函数的values数组,这个数组既可以被内部函数_calc调用,也可以在自己函数体内调用;第一层函数的关键点在于返回的是在自己内部定义的那个函数_calc,这个hack就能让Add函数在外部能访问函数体内的一直保存的values数组,进一步说这种方法可以得到第一层函数任何一个声明的变量,这是闭包的一个很常见的用法(返回函数)。

理解下面这些话

JS中的闭包就是函数可以访问父作用域(这个总结够短的,有争议的可以先搁置争议)

上面其实可以看做是自执行的匿名函数(_calc可以它看成一个匿名函数输出来),自执行的函数实际上是高阶函数的一种形式。高阶函数就是以其它函数为输入,或者返回一个函数为输出的函数(这个理解越来越像是闭包的理解了)

所以函数将其他函数作为输入参数或者作为返回值,就可以称为高阶函数

上面其实还需要了解一个知识点就是纯函数

//非纯函数
var wscats1 = function(str) {
    window.innerHeight;
    window.innerWidth;
    return str + 's innerHeight: ' + window.innerWidth + 'px';
};
//纯函数
var wscats2 = function(str, height) {
    return str + 's innerHeight: ' + height + 'px';
};
console.log(wscats1('wscats'));
console.log(wscats2('wscats', 254));

两个函数的区别在于非纯函数依赖window这个全局对象的状态来计算宽度和高度,而自给自足的纯函数则要求这些值作为参数传入,当一个函数是纯的,也就是不依赖于当前状态和环境,我们就不用管它实际的计算结果是什么时候被计算出来。

纯函数返回的计算结果仅与传入的参数相关

纯函数是完完全全独立的,所以它适合被重复调用使用,为下面的函数编程做一个好铺垫

有更深刻理解或者有误的话请务必提醒并留言我~

回到题目核心的JS函数编程
由于JS中,函数是头等公民,这里的实际意思就是函数被认定最基本最根本的类型,正如数字和对象一样。 如果数字和对象可以被来回传递,那么函数(函数我为什么就不可以呢)也可以的。
这就是JS函数编程的基本**吧

那下面第四段代码就是在第三段代码后面的基础上再添加,实现这种思路

var calc2 = Add();
var objects = [obj1, obj2, obj3]; // 这个数组可以很大
objects.forEach(calc2);
//注意对象foeEach的用法 array.forEach(callbackfn[, thisArg]);对于数组中的每个元素,forEach都会调用callbackfn函数一次
console.log(calc2());

这里的calc2函数就变成一个‘变量’进入到forEach参数内

我们可以在这个基础上继续扩充功能,第五段代码,链式调用

objects1 = objects.reverse();
objects2 = objects1.concat([4, 16]);
objects3 = objects2.map(Math.sqrt);
console.log(objects3);

用jQuery用多了,就会知道,jq习惯把类似上面的代码链式调用
类似这样把代码串在一起

console.log(['eno', 'wscats', 'yao'].reverse().concat([4, 16]).map(Math.sqrt)); //NaN,NaN,NaN,2,4
//写明显一点
console.log(
    ['eno', 'wscats', 'yao']
    .reverse()
    .concat([4, 16])
    .map(Math.sqrt)
);

这样用的前提必须是这个函数对象里面拥有这个方法,换句话说就是Array.prototype有这个方法(例如reverse, concat ,map)
给他扩充一个简单的方法如下:

Array.prototype.make4 = function() {
    console.log(this[4]);
    //this.length=4;
    return this[4];
}
console.log(['eno', 'wscats', 'yao'].reverse().concat([4, 16]).map(Math.sqrt).make4()); //4

上面除了展示匿名函数,链式调用,还有有一种方法是函数式编程里面常用的,就是递归

var Add2 = function(n) {
    if (n < 0) {
        // 基准情形
        return 'I am wscats';
    } else {
        // 递归情形
        return Add2(n - 1);
    }
}
console.log(Add2(5));

注意,基准情形就是让递归停止下来的条件,防止无限递归

再复杂点就叫分而治之,其实数学界很多问题都可以用递归解决的

var Add3 = function(a, b) {
    if (a < 0 && b == 8) {
        console.log(a % b); //-1
        // 基准情形
        return 'I am wscats';
    } else {
        // 递归情形
        return Add3(a - 1, b);
    }
}
console.log(Add3(5, 8));
console.log(Add3(5, 7)); //Maximum call stack size exceeded

Javascript获取经纬度,关于调用百度API的问题

<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=XXXXXXXXXXXXX"></script>
上面这段代码执行完后百度会返回一段代码并插入到你的DOM中执行

<script>
    (function() {
            window.BMap_loadScriptTime = (new Date).getTime();
            document.write('<script type="text/javascript" src="http://api.map.baidu.com/getscript?v=2.0&ak=XXXXXXXXXXX&services=&t=20160513110936"></script>');})();
</script>

image
首先记得引入百度api请求脚本,ak要换上自己的

var setCookie = function(name, value) {
    var Days = 30;
    var exp = new Date();
    exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
    document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString();
};
var geolocation = new BMap.Geolocation();
//弹出地理授权
geolocation.getCurrentPosition(function(r) {
        if (this.getStatus() == BMAP_STATUS_SUCCESS) {
            alert('定位成功');
            console.log(r.point);
        } else {
            alert("baidu return failed");
        }
    },
    //获取失败时候的回调
    function(r) {
        console.log(r);
        alert('定位失败');
        return {
            //设置高精度
            enableHighAccuracy: true
        };
    }
);

在这个函数执行的时候,经过测试其实不管你是否允许地理位置的授权,都能获取到你的定位位置,只不过不授权时候获取的应该是IP地址的定位,授权是精确的GPS定位而已

所以这里的问题在于如果是这样的话,那在任何一段Javascript脚本中执行上述代码,用户在知道弹出询问是否允许获取地理位置授权这个提示之后,不管是否允许都能获取到使用者的定位的

这里还要注意几个地方就是geolocation是HTML5的东西,一般移动端支持都比较好的,用到的时候最好判断一下浏览器是否支持

if ("geolocation" in navigator) {
    alert("支持geolocation");
} else {
    alert("不支持geolocation");
}

还有百度geolocation.getCurrentPosition()这个函数里面的第一个参数是回调成功后执行的,第二个参数是回调后失败执行的,我看官网的地址貌似没有写明白第二个参数是可以传一个对象或者匿名函数进去,所以导致很多人不知道getCurrentPosition失效的时候怎么捕捉到这个失败的回调,我上面的例子就是传入一个失败时候要执行回调函数,让后面代码能顺利执行下去

如果确实因为各种原因而获取不到回调,建议这里加个定时器比较保险,免得program在这里白白被卡死

setTimeout(function() {
    alert("获取超时");
    setCookie("longitude", '113.333333');
    setCookie("latitude", '23.333333');
    setCookie("city", 'wscats');
    setCookie("cityCode", '543210');
    load();
}, 4000)

其实百度的Geolocation跟HTML5 Geolocation(地理定位)也是非常相似的,看下面的例子

<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0,user-scalable=no">
<body>
        <p id="demo">点击按钮获取您当前坐标(可能需要比较长的时间获取):</p>
        <button onclick="getLocation()">点我</button>
        <div id="mapholder"></div>
        <script>var x = document.getElementById("demo");

function getLocation() {
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(showPosition, showError);
    } else {
        x.innerHTML = "该浏览器不支持获取地理位置。";
    }
}

function showPosition(position) {
    var latlon = position.coords.latitude + "," + position.coords.longitude;
    var img_url = "http://maps.googleapis.com/maps/api/staticmap?center=" + latlon + "&zoom=14&size=400x300&sensor=false";
    document.getElementById("mapholder").innerHTML = "<img src='" + img_url + "'>";
}

function showError(error) {
    console.log(error);
    switch (error.code) {
        case error.PERMISSION_DENIED:
            x.innerHTML = "用户拒绝对获取地理位置的请求。"
            break;
        case error.POSITION_UNAVAILABLE:
            x.innerHTML = "位置信息是不可用的。"
            break;
        case error.TIMEOUT:
            x.innerHTML = "请求用户地理位置超时。"
            break;
        case error.UNKNOWN_ERROR:
            x.innerHTML = "未知错误。"
            break;
    }
}</script>
</body>
</html>

image

这里的navigator.geolocation.getCurrentPosition()也是传入两个函数,一个关于回调成功,一个关于回调失败的,测试的过程中发现一直返回给我
位置信息是不可用的。
上网查了原因,应该是:鉴于该特性可能侵犯用户的隐私,除非用户同意,否则用户位置信息是不可用的。还有可能是因为某些浏览器调用了谷歌的服务,所以这里会被卡了

Angular的run方法巧妙运用

1.浏览器判断
在angular做微信应用的时候,有时候我们也想把相同一份代码运行在非微信的浏览器上,这时候我们可以在angular的run上写点东西实现~
例如asw.run函数里执行定义一个$rootScope.isWeiXinLogin的函数

.run(['$rootScope', '$route', '$window', '$location', 'Position', '$cookies', 'Request', '$cookieStore',
    function($rootScope, $route, $window, $location, position, $cookies, request, $cookieStore) {
        //非微信的登陆
        $rootScope.isWeiXinLogin = function() {
            //判断是否微信登陆
            var ua = window.navigator.userAgent.toLowerCase();
            //console.log(ua); //mozilla/5.0 (iphone; cpu iphone os 9_1 like mac os x) applewebkit/601.1.46 (khtml, like gecko) version/9.0 mobile/13b143 safari/601.1
            if (ua.match(/MicroMessenger/i) == 'micromessenger') {
                console.log("  是来自微信内置浏览器");
                return true;
            } else {
                console.log("不是来自微信内置浏览器");
                return false;
            }
        };
]);

这样它能在应用的其他部分之前提前被执行,然后根据$rootScope.isWeiXinLogin的返回我们可以在不同的视图或者控制器有效的进行判断是否为微信浏览器

angular.module('wscats').controller('OrderCtrl', ['$rootScope', '$scope', 'Request', '$cookies', '$window', '$routeParams', '$location', 'Tool',
    function($rootScope, $scope, request, $cookies, $window, $routeParams, $location, tool) { 
        if ($rootScope.isWeiXinLogin()) {
                // code...
        }
    }
]);

2.登陆判断
在run里面写登陆判断是一种不错的方案,例如下面我写的这段,配合cookie和我上面的浏览器判断,当我加载页面的时候我就可以调用$rootScope.goLogin方案来判断是否这个路由所在的视图为登陆,如果有这个合法cookie就让它继续运行,不然则返回login页面进行登陆~

$rootScope.goLogin = function(replace) {
    if ($rootScope.isWeiXinLogin()) {
        if (!replace) {
            $cookieStore.remove('loginBack');
            delete $cookies.loginBack;
            $location.path('login');
        } else {
            $cookies.loginBack = $location.path();
            $location.path('login').replace();
        }
    } else {
        $cookieStore.remove('loginBack');
        delete $cookies.loginBack;
        $location.path('loginWebapp');
    }
};

3.白名单设置
曾经写过一个这样的函数来实现路由的参数判断,来设置白名单,那时候这个函数还放在全局变量里面~其实回头想想算是不大好的方法

var getParam = function(name) {
    var search = document.location.search;
    var pattern = new RegExp("[?&]" + name + "\=([^&]+)", "g");
    var matcher = pattern.exec(search);
    var items = null;
    if (null != matcher) {
        try {
            items = decodeURIComponent(decodeURIComponent(matcher[1]));
        } catch (e) {
            try {
                items = decodeURIComponent(matcher[1]);
            } catch (e) {
                items = matcher[1];
            }
        }
    }
    return items;
};
//这个是根据路由name来决定进入那个parts
window.cats = getParam('WsCats');

后来改进了下面这个简单的例子,就可以不用用上面那句代码来实现了

$rootScope.$on('$routeChangeSuccess',
    function() {
        var route = window.location.href;
        if (route.indexOf('/hello/') != -1 && route.indexOf('/wscats/') != -1) {
            window.WsCatsShareUrl = window.location.href;
        } else if (route.indexOf('#/index') != -1) {
            window.WsCatsShareUrl = window.location.href;
        } else if (route.indexOf('#/asw'scat/') != -1) {
            window.WsCatsShareUrl = window.location.href;
        } else {
            //跳转下载页面
            window.WsCatsShareUrl = '~wscats~.cn';
        }
);

上面我们根据路由发生的变化进行白名单的设置,复杂点的话可以运用一下正则,这样就能很好的过滤我们禁止的url,由于例子就不写这么复杂啦~

4.设置公共参数
这个其实就不用写例子了,因为上面的例子也算是这个的一部分吧~
后面有时间慢慢补充~

Javascript滑屏切换场景

滑屏切换的效果,关键是transform动画实现,动画运行后再hide隐藏实现,配合移动端一些滑动手势库可以简单实现一些幻灯片效果

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>

    <body>
        <div class="page page1 page_moveToTop">
            <div class="wrap">
            </div>
        </div>
        <div class="page page2 page_moveFromBottom">
            <div class="wrap">
            </div>
        </div>
    </body>
    <style>
        body,
        div {
            margin: 0;
            padding: 0;
        }

        .page {
            width: 100%;
            height: 100%;
            position: absolute;
            font-size: 100px;
            text-align: center;
        }

        .page1 {
            background-color: #CCCCCC;
            background-size: cover;
        }

        .page2 {
            background-color: #FFFF66;
            background-size: cover;
        }

        .wrap {}

        .page_moveToTop {
            -webkit-animation: moveToTop .6s ease both;
            animation: moveToTop .6s ease both;
        }

        @keyframes moveToTop {
            from {}
            to {
                -webkit-transform: translateY(-100%);
                transform: translateY(-100%);
            }
        }

        .page_moveFromBottom {
            -webkit-animation: moveFromBottom .6s ease both;
            animation: moveFromBottom .6s ease both;
        }

        @keyframes moveFromBottom {
            from {
                -webkit-transform: translateY(100%);
                transform: translateY(100%);
            }
        }
    </style>
</html>

微信公众号开发

首先去微信公众号官网
注册订阅号
微信接入官方文档
这里写图片描述
这里写图片描述
在新浪服务器下新建一个SAE应用
这里写图片描述
配置服务器,并把地址带到公众号的
这里演示的是用新浪SAE云服务器
新浪云

这里写图片描述
在新浪云的代码管理中把链接粘贴到微信的基本配置中的URL输入框
可参考官方token的设置方法
或者用下面的代码

<?php
/**
 * wechat php wsscat test
 */

//define your token
define("TOKEN", "weixin");
$wechatObj = new wechatCallbackapiTest();
if (isset($_GET['echostr'])) {
    $wechatObj -> valid();
} else {
    $wechatObj -> responseMsg();
}

class wechatCallbackapiTest {
    public function valid() {
        $echoStr = $_GET["echostr"];

        //valid signature , option
        if ($this -> checkSignature()) {
            echo $echoStr;
            exit ;
        }
    }

    public function responseMsg() {
        //get post data, May be due to the different environments
        $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];

        //extract post data
        if (!empty($postStr)) {
            /* libxml_disable_entity_loader is to prevent XML eXternal Entity Injection,
             the best way is to check the validity of xml by yourself */
            libxml_disable_entity_loader(true);
            $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            $fromUsername = $postObj -> FromUserName;
            $toUsername = $postObj -> ToUserName;
            $keyword = trim($postObj -> Content);
            $time = time();
            $textTpl = "<xml>
                            <ToUserName><![CDATA[%s]]></ToUserName>
                            <FromUserName><![CDATA[%s]]></FromUserName>
                            <CreateTime>%s</CreateTime>
                            <MsgType><![CDATA[%s]]></MsgType>
                            <Content><![CDATA[%s]]></Content>
                            <FuncFlag>0</FuncFlag>
                            </xml>";

            if (!empty($keyword)) {
                $msgType = "text";
                $contentStr = "Welcome to wechat world! I'am wsscat,nice to meet you";
                $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                echo $resultStr;
            } else {
                echo "Input something...";
            }

        } else {
            echo "";
            exit ;
        }
    }

    private function checkSignature() {
        // you must define TOKEN by yourself
        if (!defined("TOKEN")) {
            throw new Exception('TOKEN is not defined!');
        }

        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];

        $token = TOKEN;
        $tmpArr = array($token, $timestamp, $nonce);
        // use SORT_STRING rule
        sort($tmpArr, SORT_STRING);
        $tmpStr = implode($tmpArr);
        $tmpStr = sha1($tmpStr);

        if ($tmpStr == $signature) {
            return true;
        } else {
            return false;
        }
    }

}
?>

token可以自行更改,现在这里我用的是weixin

  • URL:应用所在服务器地址[云应用的域名]
  • Token:填weixin即可 [服务器中设置]
  • EncodingAESKey:随机生成即可
    这里写图片描述

设置成功后微信的服务器配置项会变成如下

这里写图片描述
配置成功后按启动

如果是官方的示例代码,再启动成功后记得再更改服务器上面的这部分代码为下面,让它接受信息后调用responseMsg()函数

//define your token
define("TOKEN", "weixin");
$wechatObj = new wechatCallbackapiTest();
if (isset($_GET['echostr'])) {
    $wechatObj -> valid();
} else {
    $wechatObj -> responseMsg();
}

在微信搜索或者扫描二维码并关注公众号即可看到以下回复
这里写图片描述

来到这一步我们可以接入图灵测试的接口来测试
图灵机器人接口

public function responseMsg() {
        //get post data, May be due to the different environments
        $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];

        //extract post data
        if (!empty($postStr)) {
            /* libxml_disable_entity_loader is to prevent XML eXternal Entity Injection,
             the best way is to check the validity of xml by yourself */
            libxml_disable_entity_loader(true);
            $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            $fromUsername = $postObj -> FromUserName;
            $toUsername = $postObj -> ToUserName;
            $keyword = trim($postObj -> Content);
            $time = time();
            $textTpl = "<xml>
                            <ToUserName><![CDATA[%s]]></ToUserName>
                            <FromUserName><![CDATA[%s]]></FromUserName>
                            <CreateTime>%s</CreateTime>
                            <MsgType><![CDATA[%s]]></MsgType>
                            <Content><![CDATA[%s]]></Content>
                            <FuncFlag>0</FuncFlag>
                            </xml>";

            if (!empty($keyword)) {
                $apiKey = "输入自己的apiKey";
                $apiURL = "http://www.tuling123.com/openapi/api?key=KEY&info=INFO";
                $reqInfo = $keyword;
                $url = str_replace("INFO", $reqInfo, str_replace("KEY", $apiKey, $apiURL));
                $res = file_get_contents($url);
                $resObj = json_decode($res);
                $contentStr = $resObj->text;
                $msgType = "text";
                //$contentStr = "Welcome to wechat world! I'am wsscat,nice to meet you";
                $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                echo $resultStr;
            } else {
                echo "Input something...";
            }

        } else {
            echo "";
            exit ;
        }
    }

这里如果要回复非文本的消息我们就要更改回复的XML格式
首先我们先在代码中打印后台接受微信的**$postObj**数据,里面包括ToUserName,FromUserName,CreateTime,MsgType,Content和MsgId这几个重要信息,后面我们封装回复的消息体时候就会用到
$contentStr = json_encode($postObj)
然后包装返回的信息格式,格式是XML的格式
被动回复用户消息
比如图文的模版

$textTpl = "<xml>
                            <ToUserName><![CDATA[%s]]></ToUserName>
                            <FromUserName><![CDATA[%s]]></FromUserName>
                            <CreateTime>%s</CreateTime>
                            <MsgType><![CDATA[%s]]></MsgType>
                            <ArticleCount>2</ArticleCount>
                            <Articles>
                            <item>
                            <Title><![CDATA[你好,我是Wsscat]]></Title> 
                            <Description><![CDATA[谢谢您的关注]]></Description>
                            <PicUrl><![CDATA[http://avatar.csdn.net/1/E/E/2_qq_27080247.jpg]]></PicUrl>
                            <Url><![CDATA[http://blog.csdn.net/qq_27080247]]></Url>
                            </item>
                            <item>
                            <Title><![CDATA[Wsscat是一只猫]]></Title>
                            <Description><![CDATA[这是我第一条发布的信息]]></Description>
                            <PicUrl><![CDATA[http://avatar.csdn.net/1/E/E/2_qq_27080247.jpg]]></PicUrl>
                            <Url><![CDATA[http://blog.csdn.net/qq_27080247]]></Url>
                            </item>
                            </Articles>
                            </xml>";

                $msgType = "news";
//              $contentStr = json_encode($postObj);
                $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType);
                echo $resultStr;

图片的模版

$textTpl = "<xml>
                            <ToUserName><![CDATA[%s]]></ToUserName>
                            <FromUserName><![CDATA[%s]]></FromUserName>
                            <CreateTime>%s</CreateTime>
                            <MsgType><![CDATA[%s]]></MsgType>
                            <Image>
                            <MediaId><![CDATA[IIljTxbQ7VV7HhXWZrmR361nqd9dpyncq23YewYb6NkgQdMdytYsOD3ewKmZWARN]]></MediaId>
                            </Image>
                            </xml>";
                $msgType = "image";

                $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType);
                echo $resultStr;

常见问题:
部署新浪云token验证失败

header("Content-Type:text/html; charset=utf-8");//添加这行
/**
  * wechat php test
  */

//define your token
define("TOKEN", "weixin");
$wechatObj = new wechatCallbackapiTest();
$wechatObj->valid();
if ($this -> checkSignature()) {
            header('content-type:text');//再添加这行
            echo $echoStr;
            exit ;
        }

sessionstorage,localstorage和cookie

cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下。存储大小限制也不同,cookie数据不能超过4k,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识。

sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大。数据有效期不同,
sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;
localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;
cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。作用域不同,sessionStorage不在不同的浏览器窗口**享,即使是同一个页面;
localStorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。Web Storage 支持事件通知机制,可以将数据更新的通知发送给监听者。Web Storage 的 api 接口使用更方便。

总结:

  • localStorage没有过期时间,只要不clear或remove,数据会一直保存。
  • sessionStorage 针对一个session进行数据存储,生命周期与session相同,当用户关闭浏览器后,数据将被删除。

响应式布局 媒体查询

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0,user-scalable=no">
首先这句代码在移动端开发是基本会出现的,它意思就是:整个网站的宽度让它默认等于屏幕宽度(width=device-width),原始缩放比例(initial-scale=1)为1.0,即网页初始大小占屏幕面积的100%。基本所有常用的浏览器都能设置这句话,IE9也是可以的。比较老的IE6、7和8就需要写另外的兼容。

  • width - viewport的宽度 height - viewport的高度
  • initial-scale - 初始的缩放比例
  • minimum-scale - 允许用户缩放到的最小比例
  • maximum-scale - 允许用户缩放到的最大比例
  • user-scalable - 用户是否可以手动缩放

媒体查询

<link href="css/wsscat.css" rel="stylesheet" type="text/css" media="screen" />
<link href="css/wsscat1.css" rel="stylesheet" type="text/css" media="all" />
<link href="css/wsscat2.css" rel="stylesheet" type="text/css" media="print" />

引入样式都有一个共同的属性mediamedia就是用来指定特定的媒体类型
如屏幕screen和打印print的样式表,当然还有其他的,比如说TV,handheld等,其中all表示的是支持所有媒体介质

and是一个关键词,还有not,only,all

<!--not关键字是用来排除某种制定的媒体类型,换句话来说就是用于排除符合表达式的设备-->
<link rel="stylesheet" media="not screen and (max-width: 1200px)" href="wsscatTv.css" type="text/css" />

only关键字意思是用来定某种特定的媒体类型,可以用来排除不支持媒体查询的浏览器
<link rel="stylesheet" media="only screen and (max-device-width:240px)" href="wsscatOnly.css" type="text/css" />

media中如果没有明确指定类型,那么其默认为all
<link rel="stylesheet" media="(min-width: 481px) and (max-width: 900px)" href="wsscat.css" type="text/css" />
上面这句没有media属性所以默认是all
文字用

CSS3选择器

0.[attribute=value]
为name等于css的元素设置样式
image

<body>
    <p name="css">Wsscat!</p>
    <p name="css-ab">Asw!</p>
    <p name="css -ab">Asw!</p>
    <p name="css-cd">cat!</p>
    <p name="cd-css">me!</p>
    <p name="em">css!</p>
</body>
<style>
    [name=css] {
        background: #666666;
    }
</style>

1.[attribute|=value]
image
选择attribute属性值以"value"开头的元素,并设置其样式:
[attribute|=value]选择器用于选取带有以指定值开头的属性值的元素。
注意的是,该值必须是整个单词,比如 name="css",或者后面跟着连字符,比如 name="css-ab"。

<body>
    <p name="css">Hello!</p>
    <p name="css-ab">Hi!</p>
    <p name="css-cd">Ello!</p>
    <p name="me">Hi!</p>
    <p name="em">nihao!</p>
</body>
<style>
    [name|=css] {
        background: #666666;
    }

    [name|=me] {
        background: #999999;
    }
</style>

2.[attribute^=value]
image
[attribute^=value]选择器匹配属性值以指定值开头的每个元素。
设置name属性值以"css"开头的所有 div元素的背景色:

<body>
    <p name="css">Hello!</p>
    <div name="css-ab">Hi!</div>
    <div name="ab-css">Hi!</div>
    <p name="css-cd">Ello!</p>
    <p name="me">Hi!</p>
    <p name="em">nihao!</p>
</body>
<style>
    div[name^=css] {
        background: #666666;
    }
</style>

3.[attribute*=value]
image
设置name属性值包含"css"的所有p元素的背景色:
[attribute*=value]选择器匹配属性值包含指定值的每个元素。

<body>
    <p name="css">Hello!</p>
    <p name="css-ab">Hi!</p>
    <p name="css-cd">Ello!</p>
    <p name="cd-css">Hi!</p>
    <p name="em">nihao!</p>
</body>
<style>
    [name*=css] {
        background: #666666;
    }
</style>

4.[attribute$=value]
image
设置name属性值以"css"结尾的所有p元素的背景色:
注意“css”这个既属于开头也属于结尾,所以选择器也是可以选择到。
[attribute$=value]选择器匹配属性值以指定值结尾的每个元素。

<body>
    <p name="css">Wsscat!</p>
    <p name="css-ab">Asw!</p>
    <p name="css-cd">cat!</p>
    <p name="cd-css">me!</p>
    <p name="em">css!</p>
</body>
<style>
    [name$=css] {
        background: #666666;
    }
</style>

5.[attribute~=value]
选择name属性包含单词"css"的元素,并设置其样式:
注意是css单词,所以css-ab这样不算
image

<body>
    <p name="css">Wsscat!</p>
    <p name="css-ab">Asw!</p>
    <p name="css -ab">Asw!</p>
    <p name="css-cd">cat!</p>
    <p name="cd-css">me!</p>
    <p name="em">css!</p>
</body>
<style>
    [name~=css] {
        background: #666666;
    }
</style>

Angular处理Html转义问题

angular用$sce服务来过滤HTML标签
先看这个教程,再往下看~
http://www.w3cscript.com/Angular/2014-11-26/1.html

$scope.shareTips = data.data;//后台返回的数据赋个值吧
$scope.shareTipss = '1、Eno Yao;<br/>2、WsCats';
$scope.shareTips.rule_content = $sce.trustAsHtml($scope.shareTips.rule_content);//这里发现还是不行,难道姿势不对吗,纠结中~
console.log($scope.shareTips.rule_content);//测试返回的是空对象
$scope.shareTips.rule_content = $scope.shareTips.rule_content.replace(/\r?\n/g, "<br />");//换用正则解决,把所有\n换成<br />,是可以的
console.log($scope.shareTips.rule_content);//这里返回的字符串已经把\n换成<br />的

这里有两点细节很重要:
首先记得用$sce.trustAsHtml要先注入

angular.module('App').controller('ShareTipsCtrl', ['$rootScope', '$scope', 'Request', '$cookies', '$window', '$routeParams', '$location', '$sce',
    function($rootScope, $scope, request, $cookies, $window, $routeParams, $location, $sce) { //测试$sce
}]);

其次就是View渲染的时候
不要用
<p>{{$scope.shareTips.rule_content}}</p>
这里返回
会被ng安全处理的
用这个吧
<p ng-bind-html="shareTips.rule_content"></p>
后面我有时间会补充中...

20160426更新
最近调试了支付宝的网页支付的时候终于把这个问题解决了
由于支付宝的demo是后台生成一个DOM来返回的,所以在ng中我直接拿这部分数据渲染到浏览器上就可以了

angular.module('AswsTest').controller('OrderAliPayCtrl', ['$rootScope', '$scope', 'Request', '$cookies', '$window', '$routeParams', '$location', 'Tool', '$sce',
    function($rootScope, $scope, request, $cookies, $window, $routeParams, $location, tool , $sce) { 
        console.log($rootScope.alipayDom.order_string);
        $scope.dom = $sce.trustAsHtml($rootScope.alipayDom.order_string);
    }
])
<div ng-bind-html="dom">
</div>

这里ng-bind-html$sce是配合使用的,实践中缺一不可记得噢
image
然后成功渲染出这个页面

CSDN博客隐藏配置

image
自定义CSS的文本框
打开浏览器找到对应的前端代码

<form id="form1" name="form1" action="/Configure/SaveConfig" method="post">
<div class="confmain">

<p>博客标题<br>
<input type="text" name="title" maxlength="50" value="WsCats" style="cursor: auto; background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABHklEQVQ4EaVTO26DQBD1ohQWaS2lg9JybZ+AK7hNwx2oIoVf4UPQ0Lj1FdKktevIpel8AKNUkDcWMxpgSaIEaTVv3sx7uztiTdu2s/98DywOw3Dued4Who/M2aIx5lZV1aEsy0+qiwHELyi+Ytl0PQ69SxAxkWIA4RMRTdNsKE59juMcuZd6xIAFeZ6fGCdJ8kY4y7KAuTRNGd7jyEBXsdOPE3a0QGPsniOnnYMO67LgSQN9T41F2QGrQRRFCwyzoIF2qyBuKKbcOgPXdVeY9rMWgNsjf9ccYesJhk3f5dYT1HX9gR0LLQR30TnjkUEcx2uIuS4RnI+aj6sJR0AM8AaumPaM/rRehyWhXqbFAA9kh3/8/NvHxAYGAsZ/il8IalkCLBfNVAAAAABJRU5ErkJggg==&quot;); background-attachment: scroll; background-position: 98% 50%; background-repeat: no-repeat;">
</p>
<p>博客描述<br>
<input type="text" name="subTitle" maxlength="100" value="秋枫的猫屋">
</p>


                  <p>编辑器类型<br>
                <select name="editType" id="editType">        
                    <option value="0" selected="">xhEditor编辑器(默认)</option>
                    <option value="1">MarkDown编辑器</option>
                </select>


</p>

<div>
<p>博客皮肤</p>
<ul class="nostyle">
<li><a href="http://blog.csdn.net/qq_27080247?skin=default" target="_blank" title="点击预览该皮肤"><img src="http://static.blog.csdn.net/skin/default/small.jpg"></a><br><span><input type="radio" name="radSkin" id="radSkin0" value="default"><label for="radSkin0">深海蓝</label></span></li><li><a href="http://blog.csdn.net/qq_27080247?skin=light_blue" target="_blank" title="点击预览该皮肤"><img src="http://static.blog.csdn.net/skin/light_blue/small.jpg"></a><br><span><input type="radio" name="radSkin" id="radSkin1" value="light_blue"><label for="radSkin1">亮彩蓝</label></span></li><li><a href="http://blog.csdn.net/qq_27080247?skin=dark1" target="_blank" title="点击预览该皮肤"><img src="http://static.blog.csdn.net/skin/dark1/small.jpg"></a><br><span><input type="radio" name="radSkin" id="radSkin2" value="dark1"><label for="radSkin2">炫酷黑</label></span></li><li><a href="http://blog.csdn.net/qq_27080247?skin=ink" target="_blank" title="点击预览该皮肤"><img src="http://static.blog.csdn.net/skin/ink/small.jpg"></a><br><span><input type="radio" name="radSkin" id="radSkin3" value="ink"><label for="radSkin3">水墨</label></span></li><li><a href="http://blog.csdn.net/qq_27080247?skin=skin-blue" target="_blank" title="点击预览该皮肤"><img src="http://static.blog.csdn.net/skin/skin-blue/small.jpg"></a><br><span><input type="radio" name="radSkin" id="radSkin4" value="skin-blue"><label for="radSkin4">技术工厂</label></span></li><li><a href="http://blog.csdn.net/qq_27080247?skin=skin-white" target="_blank" title="点击预览该皮肤"><img src="http://static.blog.csdn.net/skin/skin-white/small.jpg"></a><br><span><input type="radio" name="radSkin" id="radSkin5" value="skin-white"><label for="radSkin5">科技星空</label></span></li><li><a href="http://blog.csdn.net/qq_27080247?skin=skin-yellow" target="_blank" title="点击预览该皮肤"><img src="http://static.blog.csdn.net/skin/skin-yellow/small.jpg"></a><br><span><input type="radio" name="radSkin" id="radSkin6" value="skin-yellow"><label for="radSkin6">编程工作室</label></span></li>
</ul>
</div>
<div class="clear"></div>

<p id="codeskin">高亮代码皮肤<br>
<select name="codeStyle">
<script type="text/javascript">
    (function () {
        var names = ["default", "blue", "blue_black", "blue_green", "default_big", "default_relax", "green", "green_black", "green2_black", "header", "nobg", "nobg_code","simple","simple_dark"];
        var titles = ["默认皮肤", "蓝色关键字", "蓝色关键字,黑色背景", "蓝色关键字,绿色背景", "大字体", "加大行间距", "绿色关键字", "绿色关键字,黑色背景", "绿色字体,黑色背景", "普通窗口样式", "精简样式", "精简样式,带语言背景","精简(无行号)","精简(无行号)暗色"];

        for (var i = 0; i < names.length; i++) {
            document.writeln(csdn.format("<option value='{0}'>{1}</option>", names[i], titles[i]));
        }
    })();
</script><option value="default">默认皮肤</option>
<option value="blue">蓝色关键字</option>
<option value="blue_black">蓝色关键字,黑色背景</option>
<option value="blue_green">蓝色关键字,绿色背景</option>
<option value="default_big">大字体</option>
<option value="default_relax">加大行间距</option>
<option value="green">绿色关键字</option>
<option value="green_black">绿色关键字,黑色背景</option>
<option value="green2_black">绿色字体,黑色背景</option>
<option value="header">普通窗口样式</option>
<option value="nobg">精简样式</option>
<option value="nobg_code">精简样式,带语言背景</option>
<option value="simple">精简(无行号)</option>
<option value="simple_dark">精简(无行号)暗色</option>

</select><span style="padding-left:10px;"><a href="#" onclick="javascript:previewCode(this);return false;">预览该样式</a></span>
</p>
<div class="clear"></div>
<p style="">自定义CSS<br>
<textarea name="css" rows="10"></textarea>
</p>

<p>每页显示文章数<br>
<select name="pageSize">
<option value="5">5</option><option value="10">10</option><option value="15">15</option><option value="20">20</option>
</select>
</p>
<p style="">文章列表显示方式<br>
</p><div class="radioBox" style="">
<input type="radio" name="radShowCon" id="radShowCon0" value="1"><label for="radShowCon0">只显示标题</label>
<input type="radio" name="radShowCon" id="radShowCon1" value="0"><label for="radShowCon1">显示简介</label>
</div>
<p></p>
<p style="margin:-20px 0 0 0;">有评论是否邮件通知<br>
</p><div class="radioBox">
<input type="radio" name="radNotify" id="radNotify1" value="email"><label for="radNotify1"></label>
<input type="radio" name="radNotify" id="radNotify2" value="none"><label for="radNotify2"></label>
</div>
<p></p>


<div>
    <p class="subtit">版权声明</p>
    <input type="checkbox" id="chkCopyright" name="chkCopyright"><input id="inputCopyright" name="inputCopyright" type="text" style="width:600px;" value="本文为博主原创文章,未经博主允许不得转载。" maxlength="200">
</div>


<div class="clear"></div>

<div class="btn_area_1">
<input type="submit" class="input_btn_1" value="保存配置">
<span id="sp_note" style="color:Green; padding-left:10px;"></span>
</div>

</div>
</form>

将下图的display:none都删除则出现隐藏的配置
image
尝试在自定义CSS添加boostrap的源代码发现z暂时无法生效
http://www.bootcss.com/
image

CSS Flex布局

样式如下
用flex和百分比同样做响应式布局,效果一样,但是方法不一样
传统布局方法一般是基于盒状模型的,通过display属性,position属性和float属性达到目的。它这对于那些特殊布局非常不方便,比如,垂直居中就不容易实现。

这里header相当于一个flex的盒模型,里面的div根据这个盒做相应的改变

body {
            margin: 0;
            padding: 0;
        }

        /*flex方法*/
        .header {
            display: flex;
            /*设为Flex布局以后,子元素的float、clear和vertical-align属性将失效。*/
            flex-direction: row;
            background-color: #c5c5c5;
            width: 100%;
        }

        .header div {
            text-align: center;
            flex: 1;
        }
        /*百分比方法*/
        .headerPer{
            background-color: #c5c5c5;
            width: 100%;
            float: left;
        }

        .headerPer div{
            float: left;
            width: 25%;
            text-align: center;
        }

    </style>

    <body>
        <header class="header">
            <div>左边</div>
            <div>中间</div>
            <div>中间</div>
            <div>右边</div>
        </header>

        <header class="headerPer">
            <div>左边</div>
            <div>中间</div>
            <div>中间</div>
            <div>右边</div>
        </header>
    </body>

上面flex属性用于设置或检索弹性盒模型对象的子元素如何分配空间。比如上面就把每个div分成四个1,每个占四份之一

flex设置为1就是默认设置flex-grow,flex-shrink和flex-basis属性值

  • flex-grow 一个数字,规定项目将相对于其他灵活的项目进行扩展的量。
  • flex-shrink 一个数字,规定项目将相对于其他灵活的项目进行收缩的量。
  • flex-basis 项目的长度。合法值:"auto"、"inherit" 或一个后跟 "%"、"px"、"em" 或任何其他长度单位的数字。

flex-basis属性用于设置或检索弹性盒伸缩基准值
注意:如果元素不是弹性盒对象的元素,则 flex-basis 属性不起作用
如果用JS写的话就用这种

<script> var divs = document.getElementsByTagName("div"); console.log(divs); divs[2].style.flexBasis="200px"; </script>

注意:display: box; 是 2009版本. display: flex;是2011修订版

Angular的fromJson与toJson方法

<!DOCTYPE html>
<html ng-app="App">
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        <div ng-controller="jsonController">
            <button ng-click="fromJson()">fromJson</button>
            <button ng-click="toJson()">toJson</button>
        </div>
    </body>
    <script src="ng.js"></script>
    <script>
        angular.module("App", []).controller("jsonController", function($scope) {
            var json = '{"name":"wscats", "skill":"1"}';
            var jsonArr = '[{"name":"wscats", "skill":"2"},{"name":"wscats", "skill":"3"}]';
            var obj = {
                name: "wscats",
                skill: "4"
            }
            $scope.fromJson = function() {
                var obj = angular.fromJson(json);
                console.log(obj.name);
                var objArr = angular.fromJson(jsonArr);
                console.log(objArr[1].name);
                console.log(objArr[1].skill);
            }
            $scope.toJson = function() {
                var str = angular.toJson(obj, true);
                console.log(str);
            }
        })
    </script>
</html>

angular.fromJson()方法是把json转化为对象或者对象数组
angular.toJson()方法是把对象或者数组转化json

其实它是angular内部开放出来的其中一个常用的API
其他开放的API可以参考这个文档http://www.runoob.com/angularjs/angularjs-reference.html

image

它们在angular的源码里面是这样的,注意toJson()还可以传入一个pretty参数

function toJson(obj, pretty) {
    return "undefined" == typeof obj ? undefined : JSON.stringify(obj, toJsonReplacer, pretty ? "  " : null)
}

function fromJson(json) {
    return isString(json) ? JSON.parse(json) : json
}

看源码可以得知,其实这里用了两个关键的函数JSON.stringify()和JSON.parse
所以上面其实可以用JS的方法来实现,结果一样,只是angular把它封装成一个常用的方法,因为angular自身的框架内部也其实运用到这个方法

var obj1 = JSON.parse(json);
console.log(obj1);
var obj = angular.fromJson(json);//两者结果一样
console.log(obj);
var str = angular.toJson(obj, true);
console.log(str);

var str = JSON.stringify(obj);
console.log(str);

注意
JSON.stringify(obj, toJsonReplacer, pretty ? " " : null)里面其实可以传三个参数
第一个参数是对象或者数组,第二个参数则是可选的

第二个参数用于转换结果的函数或数组。

  • 如果第二个参数为函数,则 JSON.stringify 将调用该函数,并传入每个成员的键和值。使用返回值而不是原始值。如果此函数返回 undefined,则排除成员。根对象的键是一个空字符串:""。
  • 如果第二个参数是一个数组,则仅转换该数组中具有键值的成员。成员的转换顺序与键在数组中的顺序一样。当第一个参数也为数组时,将忽略第二个参数的数组。

第三个参数也是可选的。它向返回值 JSON 文本添加缩进、空格和换行符以使其更易于读取。

  • 如果省略第三个参数,则将生成返回值文本,而没有任何额外空格。
  • 如果第三个参数是一个数字,则返回值文本在每个级别缩进指定数目的空格。如果第三个参数大于 10,则文本缩进 10 个空格。
  • 如果第三个参数是一个非空字符串(例如“\t”),则返回值文本在每个级别中缩进字符串中的字符。
  • 如果第三个参数是长度大于 10 个字符的字符串,则使用前 10 个字符。

而angular把它第二个参数设置成toJsonReplacer,就是传给toJsonReplacer函数去执行一些判断,判断处理后的json键对应的值是否合法

function toJsonReplacer(key, value) {
    var val = value;
    return "string" == typeof key && "$" === key.charAt(0) ? val = undefined : isWindow(value) ? val = "$WINDOW" : value && document === value ? val = "$DOCUMENT" : isScope(value) && (val = "$SCOPE"), val
}

举一反三我们也可以写个自己的方法放在第二个参数里面,例如写一个把处理的数组输出全部变成大写字母的函数

var arr = ["wscats", "skill"];
$scope.toJson = function() {
    var str = angular.toJson(obj, true);
    console.log(str);
    var str = JSON.stringify(arr, replaceToUpper);

    function replaceToUpper(key, value) {
        return value.toString().toUpperCase();
    }
    console.log(str);
}

返回如图的结果
image

Javascript监听触摸事件

touchstart
在触摸开始时触发事件
touchend
在触摸结束时触发事件
touchmove
在触摸期间时触发事件
整个触摸其实经历了这么一个过程
touchstart -> touchmove ->touchmove -> … -> touchmove ->touchend

Angular操作cookies方法

var setCookie = function(name, value) {
    var Days = 30;
    var exp = new Date();
    exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
    document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString();
    //$cookies[name] = value;
};

这是用Javascript写的方法去设置

var getCookie = function(name) {
    var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
    if (arr = document.cookie.match(reg))
        return unescape(arr[2]);
    else
        return null;
}

这是用Javascript写的方法去读取
其实angular也有相应的方法去操作cookies的,加载这两个module的依赖文件

<script src="http://code.angularjs.org/1.2.9/angular.min.js"></script>
<script src="http://code.angularjs.org/1.2.9/angular-cookies.min.js"></script>

源码是这样的

function(a, b, c) {
    "use strict";
    b.module("ngCookies", ["ng"]).factory("$cookies", ["$rootScope", "$browser",
        function(a, d) {
            function e() {
                var a, e, f, i;
                for (a in h) k(g[a]) && d.cookies(a, c);
                for (a in g) e = g[a], b.isString(e) ? e !== h[a] && (d.cookies(a, e), i = !0) : b.isDefined(h[a]) ? g[a] = h[a] : delete g[a];
                if (i) {
                    i = !1, f = d.cookies();
                    for (a in g) g[a] !== f[a] && (k(f[a]) ? delete g[a] : g[a] = f[a], i = !0)
                }
            }
            var f, g = {},
                h = {},
                i = !1,
                j = b.copy,
                k = b.isUndefined;
            return d.addPollFn(function() {
                var b = d.cookies();
                f != b && (f = b, j(b, h), j(b, g), i && a.$apply())
            })(), i = !0, a.$watch(e), g
        }
    ]).factory("$cookieStore", ["$cookies",
        function(a) {
            return {
                get: function(c) {
                    var d = a[c];
                    return d ? b.fromJson(d) : d
                },
                put: function(c, d) {
                    a[c] = b.toJson(d)
                },
                remove: function(b) {
                    delete a[b]
                }
            }
        }
    ])
}(window, window.angular)

$cookies[name] = value;
这个是angular设置cookies方法
$cookieStore
提供一个被session cookies支持的键值对(字符串-对象)存储。被存入和取出的对象将自动通过angular的toJson/fromJson进行序列化/反序列化。
$cookies
提供浏览器cookies的读/写访问操作。
这两个都要引入ngCookies模块才能使用,这个模块在1.4版本之后就有了
从源码中得知$cookieStore返回了三个方法get put remove 他们分别用toJson/fromJson进行序列化/反序列化

简单的写了几个例子来测试下

<!DOCTYPE html>
<html ng-app="WsCatsApp" ng-controller="aswController">

    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <script src="http://code.angularjs.org/1.2.9/angular.min.js"></script>
    <script src="http://code.angularjs.org/1.2.9/angular-cookies.min.js"></script>

    <body>
        {{title}}
    </body>
    <script>
        var WsCatsApp = angular.module('WsCatsApp', ['ngCookies']);
        WsCatsApp.controller('aswController', function($cookies, $cookieStore, $scope) {
            $cookies.name = 'wscats';
            $scope.title = "Hello, i'm wscats :)";
            $cookieStore.put("skill", "##");
            //删除cookies
            $cookieStore.remove("name");
            //设置过期日期
            var time = new Date().getTime() + 5000;
            $cookieStore.put("cookie", "Hello wsscat", {
                expires: new Date(new Date().getTime() + 5000)
            });

            $cookieStore.put("objCookie", {
                value: "wsscat cat cat",
                age: "3",
            }, {
                expires: new Date(new Date().getTime() + 5000)
            });
            console.log($cookies);
            console.log($cookies['objCookie']);
        })
    </script>

</html>

其实平时我们这样就可以把自己需要的cookies设置进去
$cookies.name = 'wscats';
但是当我们要设置一个有效时间的时候我们就用这样的方法把它设置进去

var time = new Date().getTime() + 5000;
$cookieStore.put("cookie", "Hello wsscat", {
    expires: new Date(new Date().getTime() + 5000)
});

我们还可以进行删除等操作
$cookieStore.remove("name");

Git和SVN区别&&AMD和CMD的区别

参考文章
1.GIT是分布式的,SVN不是
2.GIT把内容按元数据方式存储,而SVN是按文件
3.GIT分支和SVN的分支不同
4.GIT没有一个全局的版本号,而SVN有
5.GIT的内容完整性要优于SVN

Git一般是命令行来完成的
git指令
SVN一般用图形化界面,因为它的图形化界面比较友好
用SVN是要下载TortoiseSVN

SVN安装完后要重启才能使用

SVN有一个绿色的钩

Git拉项目的是
git clone ssh地址

SVN拉项目是
客户端SVN checkout

Angular打印错误的minErr函数

angular minErr函数的源码是这样的,这个函数主要用来输出错误的提示信息

function minErr(module) {
    return function() {
        var message, i, code = arguments[0],
            prefix = "[" + (module ? module + ":" : "") + code + "] ",
            template = arguments[1],
            templateArgs = arguments,
            stringify = function(obj) {
                return "function" == typeof obj ? obj.toString().replace(/ \{[\s\S]*$/, "") : "undefined" == typeof obj ? "undefined" : "string" != typeof obj ? JSON.stringify(obj) : obj
            };
        for (message = prefix + template.replace(/\{\d+\}/g, function(match) {
                var arg, index = +match.slice(1, -1);
                return index + 2 < templateArgs.length ? (arg = templateArgs[index + 2], "function" == typeof arg ? arg.toString().replace(/ ?\{[\s\S]*$/, "") : "undefined" == typeof arg ? "undefined" : "string" != typeof arg ? toJson(arg) : arg) : match
            }), message = message + "\nhttp://errors.angularjs.org/1.2.1/" + (module ? module + "/" : "") + code, i = 2; i < arguments.length; i++) message = message + (2 == i ? "?" : "&") + "p" + (i - 2) + "=" + encodeURIComponent(stringify(arguments[i]));
        return new Error(message)
    }
}

从这个函数中可以学到几点东西
首先是里面这个正则
template.replace(/\{\d+\}/g)
这个正则是匹配所有"{1个或更多数字}"形式的字符串
例如

var a = "{1}233{2}";
message = a.replace(/(\{)(\d+)(\})/g, 'wscats');//加了括号方便理解

相当于
new RegExp("\{\d+\}", "g")//g代表 global match(全定匹配)

其次是return new Error(message),这个

ngMinErr = minErr("ng");
throw ngMinErr("badname","hasOwnProperty is not a valid {0} name","module");

抛出打印的异常

function checkInput(x) {
    try {
        if (isNaN(parseInt(x))) {
            throw new Error("Input is not a number.");
        }
    } catch (e) {
        document.write(e.description);
        console.log(new Error("Input is not a number."));
    }
}
checkInput("not a number");

输出如下的异常信息
Uncaught Error: [ng:badname] hasOwnProperty is not a valid module name
http://errors.angularjs.org/1.2.1/ng/badname?p0=module"

这里如果throw new Error("wscats");之后,后面的代码就会得不到执行

Angular文字折叠展开组件的原理分析

自己写了个Angular的文字折叠组件,这种组件其实很多地方都能用到效果如下
展开后的效果
image
折叠后的效果
image
先放全部代码,使用的时候只需要把自己需要展现的文字{{designer.des}}替换成自己所在路由器所需要绑定的数据即可

.directive('textfold', function() {
        return {
            restrict: 'EA',
            template: '<p style="font-size: 14px; border-left:5px solid #dddddd; padding: 15px; padding-bottom: 10px; margin-bottom: 15px; margin-top: 15px;">' + '<span id="textfold" style="display:block; overflow:hidden;">{{designer.des}}</span>' + '<br />' + '<span style="color: #1bb7ac; position: relative; bottom: 10px;" ng-click="more(this)">{{isMore}}</span>' + '</p>',
            link: function(scope, element, attrs) {
                var height;
                var maxheight;
                function textfold() {
                    height = angular.element('#textfold').height();
                    maxheight = angular.element('#textfold').height();
                }
                scope.$watch('designer.des', function(data) {
                    if (data) {
                        textfold();
                    }
                })
                var isExtend = true;
                scope.isMore = "折叠";
                scope.more = function() {
                    var minheight = 23;
                    if (isExtend) {
                        if (height >= minheight) {
                            document.getElementById('textfold').style.height = height + "px";
                            setTimeout(function() {
                                scope.more();
                            }, 1);
                            height -= 10;
                        } else {
                            scope.isMore = "查看更多...";
                            scope.$apply();
                            isExtend = !isExtend;
                            height += 10;
                        }
                    } else {
                        if (height <= maxheight) {
                            document.getElementById('textfold').style.height = height + "px";
                            setTimeout(function() {
                                scope.more();
                            }, 1);
                            height += 10;
                        } else {
                            scope.isMore = "折叠";
                            scope.$apply();
                            isExtend = !isExtend;
                            height -= 10;
                        }
                    }
                }
            }
        }
    })

下面我一句句的分析这个组件的思路
首先肯定是定义好Angular该组件化的模板和使用模式

restrict: 'EA',
            template: '<p style="font-size: 14px; border-left:5px solid #dddddd; padding: 15px; padding-bottom: 10px; margin-bottom: 15px; margin-top: 15px;">' + '<span id="textfold" style="display:block; overflow:hidden;">{{designer.des}}</span>' + '<br />' + '<span style="color: #1bb7ac; position: relative; bottom: 10px;" ng-click="more(this)">{{isMore}}</span>' + '</p>',

EA为,使用元素和属性的方法都可以在DOM里面展现这个插件,既我可以这样
<textfold></textfold>也可以这样<div textfold="Wsscat"></div>的形式来复用该组件
后面我们使用link定义一个函数

var height;
var maxheight;

这两个变量一个是此时折叠完后的高度(这个是在展开变成折叠的过程中不断发生变化的,最后折叠后就是等于minheight),一个文字完全展开时候获取的高度

function textfold() {
                    height = angular.element('#textfold').height();
                    maxheight = angular.element('#textfold').height();
                }
                scope.$watch('designer.des', function(data) {
                    if (data) {
                        textfold();
                        scope.more();
                    }
                })

这两句其实很重要的,当我们获取文字的高度时候,是必须要捕获文字的变化(文字完全渲染后的高度),所以我们用scope.$watch来捕获designer.des的变化,当data不为空,即文字已渲染后

if (data) {
                        textfold();
                    }

再去执行回调函数textfold来获取当前文字的高度,暂时试过这种方法可以获取到文字渲染后的高度
如果顺序执行而不是回调执行
angular.element('#textfold').height()
只会拿到span标签的默认高度而已
这里还可以添加个小技巧,增加一句scope.more();

if (data) {
                        textfold();
                        scope.more();
                    }

这样就可以让它页面渲染完后先展开,然后再折叠,那么我们就在进来页面的时候默认是折叠的状态了,在程序里面,写这种效果,一般是先让它文字展开获取到高度再返回成折叠状态,来达到进来页面就是折叠的文字状态效果
var isExtend = true;这句是让下面的scope.more进入第一个分支折叠状态

setTimeout(function() {
                                scope.more();
                            }, 1);

这句是一句递归,其实就相当于实现jQuery的animate的文字框伸缩动画,只是这里用了一个递归来实现不断进来判断状态从而改变height值
然后通过改变scope.isMore改变它是查看更多...还是折叠
由于这里是用DOM操作
document.getElementById('textfold').style.height = height + "px";
下面这里最好加多一句
scope.$apply();
来获取DOM的变化
不过这一句要留意会有可能出现以下错误**$digest already in progress**
image
从字面的意思可以知道,就是$digest 或者 $apply 已经在一个digest的进程里了
可以有以下解决方法
在scope.$apply();外加一个定时器

setTimeout(function() {
                                scope.$apply();
                            }, 1);

或者加个判断

if (scope.$root.$$phase != '$apply' && scope.$root.$$phase != '$digest') {
                                scope.$apply();
                            }

其实大概思路就是很简单的,其他一些地方也是很容易理解有什么好的方法欢迎推荐,或者文中有什么错漏或者不足还请多多留言告知,谢谢

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.