john's tech blog

hope is coming


  • 首页

  • 标签

  • 归档

angularjs源码剖析之minErr函数

发表于 2016-12-29 | 更新于 2019-07-07

angular1.5.8 minErr函数

minErr函数名字起的很有创意,我们可以理解为错误函数最小化包装,angularjs内部变量的命名都很规范,
这已经是是值得我们借鉴的地方。

在运行过程中如果angularjs脚本出现错误,那么在浏览器控制台我们可以看到自定义的错误信息,
这些信息就是通过这个函数输出的。

使用方法

angular.js内部代码有如下用法:

1
2
3
//ng参数代表返回的新的minErr实例使用的命名空间
//也可以当作是模块。
ngMinErr = minErr('ng') //代表是ng模块的错误

函数定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* @description 描述
* 这个对象提供一个用于angular创建富错误信息的能力
* This object provides a utility for producing rich Error messages within
* Angular. It can be called as follows:
*
* 使用方法
* var exampleMinErr = minErr('example');
* throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
*
* The above creates an instance of minErr in the example namespace. The
* resulting error will have a namespaced error code of example.one. The
* resulting error will replace {0} with the value of foo, and {1} with the
* value of bar. The object is not restricted in the number of arguments it can
* take.
*
*如果传入比需要少点的参数,那么多出来的参数会保存起来
* If fewer arguments are specified than necessary for interpolation, the extra
* interpolation markers will be preserved in the final string.
*由于数据在生成过程中会被静态解析,在minErr实例创建和调用的过程中一些约束被适当的应用。
* Since data will be parsed statically during a build step, some restrictions
* are applied with respect to how minErr instances are created and called.
* Instances should have names of the form namespaceMinErr for a minErr created
* using minErr('namespace') .
* 错误代码,命名空间和模板字符串都应该是静态字符串,不能是变量和生成的表达式
* Error codes, namespaces and template strings
* should all be static strings, not variables or general expressions.
*
* @param {string} module The namespace to use for the new minErr instance.

*在angular.js 16314行 定义了一个如下Err 其中就传入了TypeError 对象
* var $qMinErr = minErr('$q', TypeError);

* line 16413
*$qMinErr(
* 'qcycle',
* "Expected promise to be resolved with value other than itself '{0}'",
* val)

* Promise解读
* 相关链接 http://lib.csdn.net/article/angularjs/33116
* @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
* error from returned function, for cases when a particular type of error is useful.
* @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
*/


function minErr(module, ErrorConstructor) {
ErrorConstructor = ErrorConstructor || Error;//如果没有提供Error构造函数,就用默认的Error

//返回真正可以使用的函数
return function() {
var SKIP_INDEXES = 2;//跳过的索引

var templateArgs = arguments,//函数传入的参数
code = templateArgs[0],//第一个参数:错误代码
message = '[' + (module ? module + ':' : '') + code + '] ',//初始化错误消息
template = templateArgs[1],//第二个参数:模板
paramPrefix, i;

message += template.replace(/\{\d+\}/g, function(match) {
//slice {0} 就提取出0
//slice {1} 就提取出1
//比如 "Unknown provider: {0}"就提取出{0}
var index = +match.slice(1, -1),//相当于把{}去掉
shiftedIndex = index + SKIP_INDEXES;//根据参数顺序要跳过2个

if (shiftedIndex < templateArgs.length) {
// shiftedIndex 是 从 SKIP_INDEXES 开始计算的
// 因为第一个参数为 错误代码 ,第二个参数为 模板
//相当于格式化参数
return toDebugString(templateArgs[shiftedIndex]);
}

return match;
});

message += '\nhttp://errors.angularjs.org/1.5.8/' +
(module ? module + '/' : '') + code;

for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
encodeURIComponent(toDebugString(templateArgs[i]));
}

return new ErrorConstructor(message);
};
}

关联函数 toDebugString

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//输出调试信息
function toDebugString(obj) {
//用===来判断
if (typeof obj === 'function') {
return obj.toString().replace(/ \{[\s\S]*$/, '');//把函数体去掉
} else if (isUndefined(obj)) {
//调用isUndefined函数
return 'undefined';
} else if (typeof obj !== 'string') {
//不是字符串就调用serializeObject函数
return serializeObject(obj);
}
//如果是字符串
//就返回obj参数
return obj;
}

关联函数 serializeObject

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* global toDebugString: true */
function serializeObject(obj) {
var seen = [];

//关于JSON.stringify
//https://developer.mozilla.org/zh-CN/docs/Using_native_JSON
//该参数可以是多种类型,如果是一个函数,则它可以改变一个javascript对象在字符串化过程中的行为,
//如果是一个包含 String 和 Number 对象的数组,则它将作为一个白名单.
//只有那些键存在域该白名单中的键值对才会被包含进最终生成的JSON字符串中.如果该参数值为null或者被省略,则所有的键值对都会被包含进最终生成的JSON字符串中.
return JSON.stringify(obj, function(key, val) {
val = toJsonReplacer(key, val);
if (isObject(val)) {
//如果seen中已经存在该值
if (seen.indexOf(val) >= 0) return '...';//变成省略号

seen.push(val);
}
return val;
});
}

从这个函数中我们可以学到以下几点

函数内部返回函数

javascript slice的用法

javascript replace 加正则的用法

函数的包装

对象的序列化 通过JSON.stringify

log4j_cannot_log_in_tomcat

发表于 2016-12-29 | 更新于 2019-05-09

slf4j 日志配置

前几天遇到了一个奇怪的问题,log4j在程序调试时是可以输出到日志文件中的,但是放到tomcat下却只生成到了tomcat自带的日志文件stdout中

log4j 配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#开发
log4j.rootLogger=ALL,CONSOLE,FILE,OUT,FILEERROR,FILEWARN
#正式
#log4j.rootLogger=CONSOLE,FILE,OUT,FILEERROR,FILEWARN
log4j.logger.com.mchange.v2.log.MLog=ALL
og4j.logger.slick=WARN,ERROR
log4j.logger.com.ht=ALL
log4j.logger.akka.http=WARN,ERROR
log4j.logger.org.reflections=ERROR
log4j.logger.kafka=ERROR,WARN
log4j.logger.org.apache.zookeeper=ERROR,WARN
log4j.logger.org.I0Itec.zkclient=ERROR,WARN

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d %-5p [%F(%L)] %m%n

log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=./logs/db.log
log4j.appender.FILE.Append=true
log4j.appender.FILE.DatePattern='.'yyyy-MM-dd'.log'
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d %-5p [%c(%L)] %m%n
log4j.appender.FILE.MaxFileSize=10240KB
log4j.appender.FILE.MaxBackupIndex=1000
log4j.appender.FILE.Threshold = DEBUG

于是排查了几天,期间也参照tomcat官方的说法把tomcat打日志的方式改成log4j,但是后来发现这只是把tomcat的日志按照log4j的形式输出而已,
根本的问题还是没有解决,就是在上述配置的db.log或者out.log中都没有程序中输出的日志。

当看到stackoverflow上得这个问题后,似乎有了一点眉头:

The output seems to be of the default format that Java’s standard logging framework (JUL) would emit.
So, there are two possibilities (that come to mind):
Your code imports java.util.logging.Logger, rather than org.apache.log4j.Logger.
There exists a library of some sort, in your classpath, that intercepts Log4J calls and converts them to JUL calls.

最后一句话引起了我的注意,因为我的lib中还引入了logback-classic-1.1.3.jar,logback-core-1.1.3.jar两个包。

死马当活马医,我把这两个包删除,然后再重启tomcat,结果DEBUG信息就正常写入了db.log中。

参考的资料有:
log4j和logback的冲突导致日志输出异常
JAVA日志组件系列(三)log4j+logback+slf4j的关系与调试
Logback浅析

第二篇文章提到了这点:
只有logback配置文件即可,因为log4j的输出已经委托给了slf4j(通过log4j-over-slf4j),而slf4j的默认实现是logback。

scala_import_problem

发表于 2016-12-22 | 更新于 2019-05-07

scala文件导入java包

定义一个stringTest.java文件如下:

1
2
3
4
5
6
7
8

public class stringTest {

public static String queryVehicleNum(){

return "ss";
}
}

用javac命令编译此文件,在目录下会生成stringTest.class
此时我们并没有定义它的package,如果在同级目录下新建javaStringTest.scala文件如下:

1
2
3
4
5
6
7
8
object testImport {

def main(args: Array[String]){

println(stringTest.queryVehicleNum())

}
}

可以看到,此时可以直接引入stringTest类不会报错,用scala命令运行此文件会输出ss

如果在目录下新建com目录,在com目录下新建test目录,把stringTest文件放入test目录,那么再执行scala testImport.scala文件就会报如下错误:

1
2
3
4
5
6
7
8
java.lang.ClassNotFoundException: stringTest
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at Main$.main(javaStringTest.scala:9)
at Main.main(javaStringTest.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

所以此时我们要在testImport类中引入stringTest包

com.test.stringTest```
1
2

但是这样我们必须先编译scala文件,```scalac javaStringTest.scala

然后我们用java命令调用生成的testImportObj java -classpath .;scala-library.jar testImportObj

或者我们也可以用scala -i testImportObj.java命令引入scala REPL

然后我们在scala命令行输入testImportObj.main(new Array[String](3))也会输出ss字符串

scala_implicit

发表于 2016-11-19 | 更新于 2019-05-07

implicit

转换成预期的数据类型

比如你有一个方法参数类型是IndexedSeq[Char],在你传入String时,编译器发现类型不匹配,就检查当前作用域是否有从String到IndexedSeq隐式转换。

Scala在需要時會自動把整數轉換成雙精度實數,這是因為在Scala.Predef對象中定義了一個

implicit def int2double(x:Int) :Double = x.toDouble

而Scala.Predef是自動引入到當前作用域的,因此編譯器在需要時會自動把整數轉換成Double類型。

转换selection的receiver

转换selection的receiver允许你适应某些方法调用,比如 “abc”.exist ,”abc”类型为String,本身没有定义exist方法,这时编辑器就检查当前作用域内String的隐式转换后的类型是否有exist方法,发现stringWrapper转换后成IndexedSeq类型后,可以有exist方法,这个和C# 静态扩展方法功能类似。

隐含参数

隐含参数有点类似是缺省参数,如果在调用方法时没有提供某个参数,编译器会查找当前作用域是否有符合条件的implicit对象作为参数传入(有点类似dependency injection)

隐式变换也可以转换调用方法的对象

编译器看到X.method,而类型X没有定义method(包括基类)方法,那么编译器就查找作用域内定义的从X到其它对象的类型转换,比如Y,而类型Y定义了method方法,编译器就首先使用隐含类型转换把X转换成Y,然后调用Y的method。

隐式转换可以用来扩展Scala语言,定义新的语法结构,比如我们在定义一个Map对象时可以使用如下语法:

1
Map(1 -> "One", 2->"Two",3->"Three")

你有没有想过->内部是如何实现的,->不是scala本身的语法,而是类型ArrowAssoc的一个方法。这个类型定义在包Scala.Predef对象中。Scala.Predef自动引入到当前作用域,在这个对象中,同时定义了一个从类型Any到ArrowAssoc的隐含转换。因此当使用1 -> “One”时,编译器自动插入从1转换到ArrowAssoc转换。

读书品1

发表于 2016-11-07 | 更新于 2019-05-07

张伯淳

张伯淳(1242—1302),字师道,号养蒙,崇德(今浙江桐乡)人。祖父张汝昌,官至迪功郎,与石门酒官张子修为邻,两家并有池馆园林之胜,号东西园,结社觞咏,时称东西二张。父张琥,南宋嘉定十三年(1220)进士,累官朝议大夫、崇德开国男。与赵孟頫为中表,人物相望。张伯淳9岁举童子科,以父荫铨迪功郎、淮阴尉,改扬州司户参军

(元)张伯淳<<送刘东崖赴建昌总管序>>云:“以翰林学士南康刘公为建昌路总管……其笔词源浩荡橫逸,人多称之。(养蒙文集)卷二

1…333435…47

John

232 日志
43 标签
GitHub Twitter
欢迎关注我的公众号:沉迷Spring
© 2023 johnwonder
由 Hexo 强力驱动 v3.2.0
|
主题 – NexT.Pisces v7.1.1
|
0%