`
robbin
  • 浏览: 4821272 次
  • 性别: Icon_minigender_1
  • 来自: 上海
博客专栏
377a9ecd-1ea1-34ac-9530-9daa53bb2a7b
robbin谈管理
浏览量:137051
社区版块
存档分类
最新评论

静态类型语言的优势究竟是什么?

    博客分类:
  • Java
阅读更多
在参与这个讨论的过程中,产生了一个新的话题,很想和大家探讨一下:

http://www.iteye.com/topic/33890

引用
是像Java或者C#这样强类型的准静态语言在实现复杂的业务逻辑、开发大型商业系统、以及那些生命周期很长的应用中也有着非常强的优势


这是一个存在于大家心里常识了。我承认我自己在潜意识里面也觉得静态强类型语言适合开发复杂,大型系统。而弱类型脚本语言不适合开发太复杂,太大型的项目。但是在参与这个讨论过程中,我突然开始置疑这个观点,事实究竟是不是这样的呢?

先定义一下标准:

强类型语言(静态类型语言)是指需要进行变量/对象类型声明的语言,一般情况下需要编译执行。例如C/C++/Java/C#

弱类型语言(动态类型语言)是指不需要进行变量/对象类型声明的语言,一般情况下不需要编译(但也有编译型的)。例如PHP/ASP/Ruby/Python/Perl/ABAP/SQL/JavaScript/Unix Shell等等。

引用
观点一:静态类型语言因为类型强制声明,所以IDE可以做到很好的代码感知能力,因为有IDE的撑腰,所以开发大型系统,复杂系统比较有保障。


对于像Java来说,IDEA/Eclipse确实在代码感知能力上面已经非常强了,这无疑能够增加对大型系统复杂系统的掌控能力。但是除了Java拥有这么强的IDE武器之外,似乎其他语言从来没有这么强的IDE。C#的Visual Studio在GUI开发方面和Wizard方面很强,但是代码感知能力上和Eclipse差的不是一点半点。至于Visual C++根本就是一个编译器而已,羞于提及Visual这个字眼。更不要说那么多C/C++开发人员都是操起vi吭哧吭哧写了几十万行代码呢。特别是像Linux Kernel这种几百万行代码,也就是用vi写出来的阿,够复杂,够大型,够长生命周期的吧。


引用
观点二:静态语言相对比较封闭的特点,使得第三方开发包对代码的侵害性可以降到很低。动态语言在这点上表现的就比较差,我想大家都有过从网上下载某个JS包,然后放到项目代码里发生冲突的经历


也就是说静态类型语言可以保障package的命名空间分割,从而避免命名冲突,代码的良好隔离性。但是这个观点也缺乏说服力。

静态类型语言中C,VB都缺乏良好的命名空间分割,容易产生冲突,但是并没有影响他们做出来的系统就不够大,不够复杂。

而Visual C++开发的DLL版本冲突也是臭名昭著的,似乎C++的命名空间没有给它带来很大的帮助。

而动态类型语言中Ruby/Python/Perl都有比较好的命名空间,特别是Python和Perl,例如CPAN上面的第三方库成吨成吨的,也从来没有听说什么冲突的问题。

诚然像PHP,JavaScript这样缺乏命名空间的动态语言很容易出现问题,但是这似乎是因为他们缺乏OO机制导致的,而不是因为他们动态类型导致的吧?

说到大型系统,复杂业务逻辑系统,Google公司很多东西都是用python开发的,这也证明了动态类型语言并非不能做大型的复杂的系统。其实我个人认为:

动态类型语言,特别是高级动态类型语言,反而能够让人们不需要分心去考虑程序编程问题,而集中精力思考业务逻辑实现,即思考过程即实现过程,用DSL描述问题的过程就是编程的过程,这方面像Unix Shell,ruby,SQL,甚至PHP都是相应领域当之无愧的DSL语言。而显然静态类型语言基本都不满足这个要求。

那静态类型语言的优势究竟是什么呢?我认为就是执行效率非常高。所以但凡需要关注执行性能的地方就得用静态类型语言。其他方面似乎没有什么特别的优势。
分享到:
评论
65 楼 LucasLee 2007-01-02  
后来我突然想到以前的著名工具,似乎人家走得的确很到位:
VB。
它既可以解释执行,默认也是不检查变量名,类型也可以为变体。这样看起来是动态语言;(一般简单的任务,或初学者都这么用)
也可以编译执行,做语法检查,也可以声明为option explicit以强制变量必须声明,也可以声明变量的类型。这样看起来又可以是静态语言了。(一般大规模应用,都推荐这么用)

看来这个争论不是第一次出现,方案也不是现在才有。
64 楼 helloworld 2006-12-30  
看中文资料和英文资料的感觉就是不一样,英文资料的文法很清晰,而中文就不一样,要看很多遍。再说文言文,那个表达就更神了。在不同的地方使用不同的文法,编程也一样。
在大型项目中,在乎的不是绚丽的文法,设计模式之类,在乎于清晰的表达。往往我们在这类项目中从来都是用笨办法来解决问题,主要考虑到大局的因素。如果采用巧办法会给大局带来不稳定因素,我们宁愿多花点代价用笨办法实现。不知道我这样说有没有离题。因此,在大项目中往往采用相对笨拙的架构和静态语言来实现,不容易出现不可预见的东西,所有东西都是很清晰的摆在图纸上的。静态语言就像盖大楼的混凝土一样,很可靠。而动态语言就像新型建材。
不同的语言不同的表达能力,也有不同的清晰度,但是我们要表达的是什么,是逻辑。语言的表达能力由什么组成,是词汇和语法。语言最后通过执行完成了逻辑。所以静态语言只要嵌入合适的执行引擎,任何可能的表达都是可执行的(参见Buding Parse
by JAVA),说到思考与表达的关系,大部分人思考较大的问题时不会直接采用编程语言来直接表达,而是用一些逻辑图或词汇碎片。只有一些随手的东西会一边思考,一边编程,这时候往往用脚本。
63 楼 whisper 2006-12-29  
当并发和分布式变得必不可少时
动态和函数式这种天生优雅的语言就一发不可收了
62 楼 netfishx 2006-12-28  
不要只讨论静态与动态的区别,也说说强类型与弱类型的区别。
据我所知,js这种典型的动态弱类型语言也要往强类型上靠,包括ruby、python等新生代动态语言也都是强类型的。
61 楼 ken1984 2006-12-28  
动态类型语言的效率和能不能做大型系统方面,看看yahoo吧,它是用PHP写的。给你用JAVA也可能做不出来那样的性能。

另外你说那么多C/C++开发人员都是操起vi吭哧吭哧写了几十万行代码,这点现在已经改变了,eclipse都有那么多插件,VI也可以有啊,而且还有emacs,gvim这些可以选择,而且都有很多插件,对你说的代码感知能力也支持啊,也有函数补全什么的功能,楼主还是先去了解了解再说吧。
60 楼 花花公子 2006-12-07  
静态类型语言的好处应该还是规整,不过现在已经变成弱点了,毕竟敏捷开发发展也不久。问题可以变成,为什么在桌面时代,没有静态语言这么多的需求。网络时代,程序语言需要和外部的Http协议以及数据库交流,也许这就是动态类型语言大展身手的环境。

瞎说几句,网络应用主流语言从perl过渡到ruby的过程中,java横插一杠子简直就是复辟。我也因此跟着走了弯路。不过ruby动态性实在太强了,已经不是简单的面向对象的perl了。
59 楼 taowen 2006-12-07  
使用Ruby的时候,脑袋要像Ruby的解释器一样工作。因为所有的东西都是动态。而Java的静态类型呢,就像一根静态的拐杖,可以给你一些可以抓得住东西撑着。撑着你,就是靠IDE提供的静态分析的能力。类型兼容,变量名检查是静态分析。提供者和使用者的互相查找是静态分析。抽象者和具现者是静态分析。更高级的IDE,还可以通过简单的推理,得知你的某些代码是没有用上的。没有个函数在某种情况下是缺少返回值的。这些都是静态分析。静态类型给Java带来的是用IDE做静态分析的能力。
要这些静态分析能力的原因很简单。人脑不是电脑。人脑不是生来运行程序的。人会累,人会烦,人会出错,人不擅长动态模拟程序运行。所以我们要拿拐杖来撑一下,扶一把。
也许只是我老了,才会有这种想法……

btw:单元测试是大家共同的动态拐杖。
58 楼 qiezi 2006-12-07  
robbin 写道
BirdGu 写道
编译器对程序员的帮助到底有多大,这个还是要应人而异的。编译器能查出来的很多都属于打字错误,拼写错误。对于robbin来说,即使没有编译器,检查这种错误也是小菜一碟。可是对于经验不是很丰富的程序员来说,情况恐怕就大大不同了。毕竟程序员经验方面差异的一个重要方面就是Debug能力和经验的差异。对高手来说仔细读上两遍程序就能发现的错误,对一些新手来说可能会花上一两小时,这种情况我在实际项目中碰到很多次了。



真的是这样的,编译器其实能够检查出来的错误实在有限,一般来说不外乎两类:

1、语法拼写错误,变量/对象名字拼写错误
2、传入参数的类型搞错了

对于第一类来说,像Radrails已经能对ruby语法进行很好的语法检查了,语法错误就会报告的,至于变量对象名字搞错,频率到是比Java高,但是只要写过一次的变量/对象,下一次写的时候就可以autocomplete的,所以拼写错误发生的频率是比Java高,但是没有想像中那么高,而且这种错误也很容易定位。

对于第二类来说,ruby会抛出类型错误异常的,也挺好定位的。

ruby我这几个月天天用,但写一段代码往往还是有变量名、方法名写错的情况,开发效率是高,不过排错也比较麻烦,好在reload也是超快。

静态类型还是有不少好处的,昨天用我从来没用过的C#语言写了个客户端程序,调用rails编写的WEB服务,分析数据再多线程下载相应文件。现学语言现查文档,所有功能包括界面一天就完成了,还生成了一个安装程序 它的IDE实在太棒了。

C#这类静态类型语言,编译时就能检查出大部分错误,所以即便我边写程序边聊天,也能写出正确运行的程序。而ruby则不然,如果我要写出稍复杂点的数据结构(用Array, Hash组成),可能就得关掉音乐全神贯注地写,还要加很多log输出以确定取的值是正确的。

我简单比较了一下,C#即便我不熟悉,不过因为语法和C++/java相似,所以还是很容易上手,ruby目前也算是用得比较熟了,虽然没有深入,开发效率我认为ruby比C#快3倍,比java快4-5倍。代码行数肯定是ruby少多了,光是数组的map, select就可以省不少行。
57 楼 wqh 2006-12-07  
对于能不能做大型的系统来说,最重要的一点还是随着规模的扩大,人员的增多,交流和理解变得越来越重要了,有时候语言方面如果易于各层次人员理解的会占优势,也就是说,功能强大、容易理解成了一个语言选择的标准,并不一定足够灵活,呵呵,因为灵活的东西也是由不够灵活的东西实现的。
56 楼 robbin 2006-11-17  
Tin 写道
“高司令Gosling”老爷爷说静态语法检查是个很好的东西,这应该是有道理的。robbin也倾向于这是个好处。当然它带来了限制。
我想,从程序员的分布上来说:上进的是一部分,而更大部分的是并不在乎技艺的庸人(千万别生气,放眼身边很多公司的确如此)。
对于在思考的人,动态带来了很多好处,不用列举,用过都知道。但是如果让所有的程序员都用动态语言,那么就可怕了,没有了静态检查,低级错误就多了好多,一个拼写失误可能就是一天的调试时间……

Java比较容易拿个AST就检查好大部分错误,运行期错误可以通过单元、组件、系统测试挽回,对测试的压力相对小。而动态语言,由于运行时推断绑定等特性,拿个AST检测不到很多错误,对测试压力就大了,对开发者对QA的要求就会高一些。这样说,动态语言是不是应该和过程改进结合起来实施?



我是相当反对认为静态类型检查是好东西,可以提高大型应用生产力的观点的。也反对认为动态语言会带来很多低级错误的观点。



55 楼 Tin 2006-11-17  
“高司令Gosling”老爷爷说静态语法检查是个很好的东西,这应该是有道理的。robbin大叔也倾向于这是个好处。当然它带来了限制。
我想,从程序员的分布上来说:上进的是一部分,而更大部分的是并不在乎技艺的庸人(千万别生气,放眼身边很多公司的确如此)。
对于在思考的人,动态带来了很多好处,不用列举,用过都知道。但是如果让所有的程序员都用动态语言,那么就可怕了,没有了静态检查,低级错误就多了好多,一个拼写失误可能就是一天的调试时间……

Java比较容易拿个AST就检查好大部分错误,运行期错误可以通过单元、组件、系统测试挽回,对测试的压力相对小。而动态语言,由于运行时推断绑定等特性,拿个AST检测不到很多错误,对测试压力就大了,对开发者对QA的要求就会高一些。这样说,动态语言是不是应该和过程改进结合起来实施?
54 楼 dongbin 2006-11-16  
引用
最简单,最快捷。
这个东西很难下定义,如果比简单,似乎jsp+javabean,比mvc都简单多了。可实际上玩java的人却都在推复杂的东西。


这个例子有些牵强,jsp+javabean结构存在大量冗余,不能算快捷,这也是框架存在的原因。
53 楼 ssuupv 2006-11-16  
我有朋友在开发一个CRM。开发这个项目开发人员有70多个人,他每天接到任务只是按文档(专门有个系统分析组写程序总体结构。并且出设计文档)上标准实现就行(专门写EJB(sessionbean)调用hibernate 根本不用考虑其它的方面),不必知道他以后用在什么,也不可能知道。反正按照标准写。然后,他们有个组专门对这些组件装配。然后给测试部门测试。
52 楼 xyz20003 2006-11-16  
最简单,最快捷。
这个东西很难下定义,如果比简单,似乎jsp+javabean,比mvc都简单多了。可实际上玩java的人却都在推复杂的东西。
51 楼 robbin 2006-11-15  
抛开这个话题,随便谈谈对于项目开发的看法:

一个新手,让他用PHP,还是Java,他肯定觉得PHP好用,我一个搞photoshop的朋友,中专毕业,PHP都用的比我熟,给别人开发了好多个网站了,可他拜我为师学习Java好几年了,至今没有入门。

最近我看到一些团队开发的情况,非常有感触。其实真的项目周期压力一大,任务一多,那个时候最简单最快捷的东西就是最顶用的。

50 楼 robbin 2006-11-15  
BirdGu 写道
编译器对程序员的帮助到底有多大,这个还是要应人而异的。编译器能查出来的很多都属于打字错误,拼写错误。对于robbin来说,即使没有编译器,检查这种错误也是小菜一碟。可是对于经验不是很丰富的程序员来说,情况恐怕就大大不同了。毕竟程序员经验方面差异的一个重要方面就是Debug能力和经验的差异。对高手来说仔细读上两遍程序就能发现的错误,对一些新手来说可能会花上一两小时,这种情况我在实际项目中碰到很多次了。



真的是这样的,编译器其实能够检查出来的错误实在有限,一般来说不外乎两类:

1、语法拼写错误,变量/对象名字拼写错误
2、传入参数的类型搞错了

对于第一类来说,像Radrails已经能对ruby语法进行很好的语法检查了,语法错误就会报告的,至于变量对象名字搞错,频率到是比Java高,但是只要写过一次的变量/对象,下一次写的时候就可以autocomplete的,所以拼写错误发生的频率是比Java高,但是没有想像中那么高,而且这种错误也很容易定位。

对于第二类来说,ruby会抛出类型错误异常的,也挺好定位的。
49 楼 bigpanda 2006-11-15  
floating 写道
特别是开发人员一多,就没法一个一个的去煽动。不知道各位大大在实践的时候怎么做好这一点的?


不好好干活,那种推一下,才动一动的人,统统滚蛋。自己不求上进的人无可救药。
48 楼 floating 2006-11-15  
robbin 写道

像XP就是比较适合RoR的软件开发方法阿,先讨论userstory,制订userstory card,然后一个card一个card进行小步骤迭代实现。

其实我觉得XP对开发人员技术素质要求不高,但是像沟通能力,团队协作的习惯要求比较高。

我在项目里曾经尝试过XP,但是感觉不是很成功。觉得XP对项目人员的斗志、激情和投入程度要求比较高(对能力方面的确像robbin说的那样要求不算高),特别是开发人员一多,就没法一个一个的去煽动。不知道各位大大在实践的时候怎么做好这一点的?
47 楼 bigpanda 2006-11-15  
robbin 写道

我用Eclipse已经有三年了,但是Eclipse的很多功能我到现在都没有掌握,调试器也只不过会最基本的功能而已。你说那么普遍的字符串拼接SQL的错误,Java里面还不是一大把吗?难道Eclipse可以限制初学者不用?HTTP Session里面随便乱塞结果集Eclipse可以避免?数据库连接不关闭Eclipse可以编译检查出来?

这些web开发当中最容易犯的错误,反而是脚本语言不容易出现,为啥呢?因为脚本语言是进程模型,不需要关闭数据库连接,内存当做不保存Session,所以Session可以随便乱塞东西,SQL拼接都提供了很强的字符串检查和过滤机制。

这不是我说瞎话,我帮很多Java系统做过性能排查和调优的咨询,Java新手的不良编程系统破坏力是及其惊人的,而PHP新手很少会在这种事情上遇到困扰。



robbin,你说的这些东西我都同意,但这已是动态静态语言之外的东西了,php没有VM,Java有VM,数据库连接和Session都是因为VM的后果,和动静态语言本身无关吧。你以前写过这方面的帖子的,我仔细读过。

我上大学的时候是用过C写过CGI程序的,编译执行,和php一样是进程模型。

要是写一个逻辑很复杂,代码量非常大的程序,一个菜鸟用php,一个菜鸟用java,我的假设是菜鸟用java敲出来的可能性还是大的。要是有人对这个论点感兴趣可以去求证或证伪去。

我去学Lisp了。

46 楼 fixopen 2006-11-15  
我觉得争论静态类型或者动态类型有点没有落到实质上。之所以我们现在觉得静态类型系统让我们束手束脚,原因是它还不够灵活,而且,在绝大多数场合,静态类型要跟动态类型结合起来才能完善的表达概念。

现代的类型系统虽说已经发育的比较丰富了,但还远不够完善,所以,现在的实质问题是我们类型系统是否能够强大到检测出它应该监测出的问题,同时灵活到不限制我们的思路。

相关推荐

    弱类型语言允许将一块内存看做多种类型

    - **C 和 C++**:尽管 C 和 C++ 被认为是静态类型语言,但在某些情况下它们表现出了弱类型行为。例如,可以直接将整型变量与字符变量相加,而无需显式类型转换。 - **Perl 和 PHP**:这两种语言都是典型的弱类型语言...

    国内首个实现纯浏览器内编译、执行全链路的自研静态类型的编译型通用编程语言 为 WebAssembly 而生的通用编程语言

    静态类型语言要求在编译时声明变量类型,这有助于防止运行时类型错误,提高代码的稳定性和性能。 3. **浏览器内的编译和执行**:在浏览器内完成编译和执行可以减少对服务器的依赖,提高用户体验,因为用户无需等待...

    cpp-Ravi编程语言是Lua53的一个衍生有限的可选静态类型

    Ravi编程语言是一种基于Lua 5.3的衍生语言,其设计目的是在保留Lua的简洁性和易用性的同时,引入了有限的可选静态类型。这使得Ravi在某些方面比纯动态类型的Lua更适合大型项目或性能敏感的应用,因为静态类型可以...

    通用首页静态化工具-可以把首页转化为静态页面

    在IT行业中,静态页面与动态页面是两种常见的网页类型。静态页面是由HTML、CSS和JavaScript等静态文件组成的,内容在服务器端创建后直接发送到客户端,而动态页面则是在用户请求时由服务器端的脚本(如PHP、ASP、JSP...

    Python-Pyre是Facebook推出Python的静态类型检查工具

    **什么是静态类型检查?** 静态类型检查是在程序执行前进行的一种分析,它通过检查源代码中的类型声明来发现潜在的类型错误。与动态类型检查(Python默认的类型检查方式)不同,动态类型检查是在运行时进行的,这...

    cpp-Empirical是用于时间序列分析的一种语言拥有静态类型的Dataframe

    cpp-Empirical是一种专门针对时间序列分析的语言,它在C++的基础上构建,提供了静态类型的DataFrame数据结构。这个框架的设计目标是为科学家、工程师和分析师提供一个高效且易于使用的工具,以便处理和理解时间相关...

    Kotlin编程语言,一种开源的静态类型编程语言,从入门到精通

    Kotlin编程语言,作为近年来备受关注的开源静态类型编程语言,已经在软件开发领域崭露头角。它由著名软件公司JetBrains发起,并且得到了全球开发者社区的广泛支持和贡献。Kotlin的设计理念旨在提高开发效率,降低...

    JetBrains Kotlin ,一种开源的静态类型编程语言,由 JetBrains 和开源贡献者支持和开发

    JetBrains Kotlin ,一种开源的静态类型编程语言,由 JetBrains 和开源贡献者支持和开发,支持多平台编程是 Kotlin 的主要优势之一。它减少了为不同平台编写和维护相同代码所花费的时间 ,同时保留了本机编程的灵活性...

    Kotlin静态类型共享和重用Kotlin静态类型共享和重用Kotlin静态类型共享和重用

    - **静态类型**:作为一种静态类型语言,Kotlin在编译阶段就进行了严格的类型检查,这有助于早期发现错误并提高代码质量。 - **类型推断**:Kotlin能够根据上下文自动推断变量的类型,这减少了显式类型声明的需求...

    各种语言比较

    这个主题通常涉及广泛,包括静态类型语言和动态类型语言的对比,编译型语言与解释型语言的区别,以及各自在性能、开发效率、可维护性等方面的表现。 描述中的“NULL”表明没有提供具体的描述,但我们可以从常见的...

    基于Java平台的多语言混合编程.pdf

    Java平台的多语言混合编程是指在Java虚拟机(JVM)上运行多种语言,包括静态类型语言和动态类型语言、命令式语言和声明式语言等。这种混合编程方式可以充分发挥Java平台的优势,实现跨平台、跨语言的开发。 Java...

    自动生成静态页面

    静态页面生成通常分为两种类型:预渲染(Prerendering)和按需生成(On-Demand Generation)。 1. 预渲染:在项目构建阶段,静态页面生成器会遍历所有路由,根据模板和数据生成对应的HTML文件。这些文件会被上传到...

    后台ASP生成前台静态网页

    在IT行业中,动态网站与静态网站是两种常见的网页类型。动态网站主要依赖服务器端脚本,如ASP(Active Server Pages),来实时生成内容并交互,而静态网站则是预先编译好的HTML页面,直接由浏览器解释执行。后台ASP...

    C++安装注意事项需要考虑三点,C++和Python语言相比的优势在哪里?.docx

    Python的语法较为宽松,支持动态类型,而C++则采用了静态类型系统,要求开发者在编写代码时明确指定类型。 2. **应用领域**:Python因其简单易学和丰富的库支持,在数据科学、机器学习、Web开发等领域受到欢迎。而...

    Atom-linter-mypy,lint python的atom linter插件可选静态类型,如pep 484中所定义.zip

    4. **静态类型检查**:与动态类型语言不同,静态类型检查是在程序运行前进行的。通过这种方式,可以在代码执行前发现类型错误,避免运行时出错。Python本身是动态类型的,但PEP 484引入了对静态类型的支持,使得...

    30编程范式游记(3)- 类型系统和泛型的本质1

    静态类型语言(如C++、Java)在编译时就确定了变量的类型,这种强制性类型检查可以在早期发现错误,提高代码安全性,并为编译器优化提供信息。相反,动态类型语言(如Python、JavaScript)在运行时才确定变量类型,...

    旅游网站静态模板

    一、静态页面的优势 1. **快速加载**:静态页面的内容在用户请求时不需要通过服务器处理,直接由浏览器解析,这使得页面加载速度更快,对于旅行者来说,快速获取信息是非常关键的。 2. **低维护成本**:静态页面的...

    java和go相比有什么优势和劣势?

    4. 静态类型:Go 是静态类型的,这在编译时就能发现类型错误,提高了代码的可靠性。 5. 简洁明了:Go 语言的语法简洁,易于学习和使用,减少了理解成本。 然而,Go 语言也有其不足: 1. 生态系统不成熟:相比于 ...

    静态(static)方法的继承和重写

    在Java编程语言中,静态方法是一种特殊类型的方法,它与类相关联,而不是与类的实例相关联。静态方法在类加载时就会被加载到内存中,因此它们可以在没有创建对象的情况下被调用。静态方法的继承和重写是面向对象编程...

    图片展示静态模板

    在IT行业中,静态页面是一种常见的网页设计类型,它主要用于创建简单、快速加载且内容不需频繁更新的网站。"图片展示静态模板"标题暗示我们这里关注的是一个专门用于展示图片的静态网页模板,这种模板通常包含精心...

Global site tag (gtag.js) - Google Analytics