Javascript实现简单的链式调用

昨天看到一篇很不错的链式调用文章,存在这里记一下。

原帖地址:http://www.css88.com/archives/4524

用过jQuery的朋友一定对jQuery中方法的链式调用印象深刻,貌似现在很多库都支持了方法的链式调用,比如YUI3等。链式调用是一个非常不错的语法特性,能让代码更加简洁、易读。很多时候链式调用可以避免多次重复使用一个对象变量。今天有人在群里说起javascript链式调用,写了几个简单的实现方式共大家参考一下:

一般我们我用函数构造一个类,例如:

var function Dog(name,age){
    this.name = name;
    this.age = age;
};
Dog.prototype={
    getName:function(){
        console.log(this.name);
    },
    getAge:function(){
        console.log(this.age);
    }
};

定义一个Dog类,并具备几个简单的方法

var dog1= new Dog("旺旺",3);
dog1.getName();
dog1.getAge();

实例化,并且调用方法。

要实现链式调用是非常简单的事情,唯一需要做的就是在每个方法后面返回this。例如:

var Dog=function(name,age){
    this.name = name;
    this.age = age;
};
Dog.prototype={
    getName:function(){
        console.log(this.name);
        return this;
    },
    getAge:function(){
        console.log(this.age);
        return this;
    }
};

var dog1= new Dog("旺旺",3);
dog1.getName().getAge();

上面的代码可以看出,Dog方法上多了一段代码:return this;

细心一点你会发现这里dog1实例前还需要一个new初始化,还是有点不方便。在改进一下:

var Dog=function(name,age){
    this.name = name;
    this.age = age;
};
Dog.prototype={
    getName:function(){
        console.log(this.name);
        return this;
    },
    getAge:function(){
        console.log(this.age);
        return this;
    }
    };
    window.Dogs=function(name,age){
    return new Dog(name,age);
};
Dogs("旺旺",3).getName().getAge();

这里在window下定义一个Dogs方法,作为Dog的别名,这样就可以直接用Dogs(“旺旺”,3).getName().getAge();这样调用了。

苛刻的网友说这样太暴露了,这样有两个全局变量变量Dog和Dogs,在改进一下:

var Dog = function(name,age){
    if(!(this instanceof Dog)){
        return new Dog(name,age);
    }
    this.name = name;
    this.age = age;
};

Dog.prototype={
    getName:function(){
        console.log(this.name);
        return this;
    },
    getAge:function(){
        console.log(this.age);
        return this;
    }
};
Dog("旺旺",3).getName().getAge();

这里在构造函数中加了这么一句:

if(!(this instanceof Dog)){
    return new Dog(name,age);
}

判断this是否为Dog实例,如果不是就创建一个新实例。

更为安全代码:

(function(){
    var Dog=function(name,age){
        if(!(this instanceof Dog)){
            return new Dog(name,age);
        }
        this.name = name;
        this.age = age;
    };
    Dog.prototype={
        getName:function(){
            console.log(this.name);
            return this;
        },
        getAge:function(){
            console.log(this.age);
            return this;
        }
    };
    return (window.Dog=Dog);
})();
Dog("旺旺",3).getName().getAge();

或者:

(function(){
    var Dog=function(name,age){
        this.name = name;
        this.age = age;
    };
    Dog.prototype={
        getName:function(){
            console.log(this.name);
            return this;
        },
        getAge:function(){
            console.log(this.age);
            return this;
        }
    };
    window.Dogs=function(name,age){
        return new Dog(name,age);
    };
})();
Dogs("旺旺",3).getName().getAge();

希望对新手有所帮助,如有不对之处欢迎留言拍砖斧正!

Textarea大小自适应

很久很久以前,七宝帮远在英国的肉肉同学重构一个网页,网页上有个留言的textarea。

由此她给我提了一个页面的问题:如何让一个textarea的大小根据内容来自动变大呢,类似微博那种。

当时研究了一下,原本的思路是计算单个文字大小高度,然后算有多少行然后调整高度。发现有兼容性问题,而且也比较麻烦。

昨天彻底的研究了一下,换了一个思路。因为DOM中,scrollHeight这个属性,可以取到该元素实际内容的高度,这样,就可以直接把textarea高度设置成scrollHeight就行了。方法简单了许多。最近在做模块化的东西,很想脱离jQuery的束缚,所以这次全部用DOM操作,做成了独立的一个js。

适用于主流浏览器,自测ie7/8/9,FireFox 10,Chrome 20.0.1096.1 通过。

Demo在此

以下是相关代码

autoTextarea.js:

var autoTextarea = function (elem)
{
    elem.style.resize = 'none';

    minHeight = elem.currentStyle ?
        parseFloat(elem.currentStyle['height']) : parseFloat(document.defaultView.getComputedStyle(elem, null)['height']);

    var adjust = function () {
        if (elem._length === elem.value.length) return;
        elem._length = elem.value.length;

        elem.style.height = minHeight + 'px';
        if (elem.scrollHeight > minHeight)
        {
            elem.style.overflowY = 'hidden';
            elem.style.height = elem.scrollHeight + 'px';
        }

    };

    addEvent = function (type, callback) {
        elem.addEventListener ?
            elem.addEventListener(type, callback, false) :
            elem.attachEvent('on' + type, callback);
    };

    addEvent('keydown', adjust);
    addEvent('keyup', adjust);
    addEvent('propertyadjust', adjust);
    addEvent('input', adjust);
    addEvent('focus', adjust);
    adjust();
};

autoTextarea.html:

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>文本框自适应</title>
    <script src="autoTextarea.js"></script>
    <style type="text/css">
        #textarea {
            width: 200px;
            height: 100px;
            overflow-y: hidden;
            border: 1px solid #000;
        }
    </style>
</head>

<body>
    <h2>请在下面的输入框输入内容测试自适应</h2>
    <textarea id="textarea"></textarea>

    <script>
        var text = document.getElementById("textarea");
        autoTextarea(text);
    </script>
</body>
</html>

Safari阅读器的实现

苹果的 Safari 5 更新了一项有趣又实用的功能——阅读器,它可以自动去掉网页的边侧栏广告,页头页尾,抓取出网页的文章内容,以更清爽的界面显示给你。用户还可以自定义文章的字体大小。这一点对我来说十分有意义,因为相比android的浏览器,iphone的safari在缩放之后,不会根据文章内容重排版面,这样一来,看网页的文章就十分痛苦,字太小,放大了网页不会重排,需要横向的拖动很不方便。现在只需点击阅读器,把字体缩放到自己喜欢的大小就可以了。更重要的,对于那种把一个文章截分成很多页的网站,它会自动检测到有下一页,当你拖动到文章最下方的时候自动加载下一页内容,连成一篇完整的内容。这样就可以对讨厌的广告说拜拜了。

Safari的阅读器

正巧,前段时间方舟项目要开发一项网页采集的功能。开发初期,网页采集类似花瓣网的图片采集。之后又新加入了视频,最后产品定位到加入新闻采集这个功能,其要求跟Safari阅读器很相似,也是提取网页的主体部分供用户编辑、采集。于是我花了一些时间来研究这个功能的实现。在完成了公司项目的需求之后,花了一周时间做成了一个单独的版本,放在自己的博客服务器上。这样一来,这个浏览器就可以摆脱浏览器的束缚,实现跨浏览器阅读了。

我的阅读器

Demo在此


这里记一点开发这个东西的经历。

对于这种新东西的开发,我的习惯是模拟人类的体验,先以最接近人类实践方式实现,再来逐步优化。对于一个新闻网页来说,当一个人眼(而不是机器或者程序)看到这个网站之后,这个人到底是怎么分辨出网页的主题文章的呢?或者说,当一个人看到网页的时候,是网页的那些特性,使得这个人明白,网页的“这一部分”就是网页的主题文章呢?一般而言(恩,对于我自己而言),最醒目,拥有最大的字体的,加粗字体,是标题。而文章一般是在标题之下且有相对大段文字的块。所以方案初定为:先找出body内拥有最大字体的标签,然后找到这个标题的兄弟节点,以及标题父亲的兄弟,然后找到字最多的,那么就认为他是文章主体了。这种最初的方式实现起来非常简单,但是效果也非常不好,因为一旦某个网页中标题不是最大字体,那么文章也会找错。比如人人网的导航栏是字体最大的,百度博客的博客名是字体最大的。而且对于结构不是预期结构的网站(实际上大多数都不会是预期的结构,例如http://news.qq.com/a/20120302/000095.htm)。标题和正文所在的标签可能会包含在不同块里。这样提取出来的会包含一些无用的评论信息和广告。所以这个方法,很快被放弃了。

QQ新闻结构

接下来我得彻底换个思路来思考这个问题,提取文章到底能不能按照人的视觉方式来?在试过很多方法,例如面积,颜色之类之后,我放弃了,毕竟人机在认识事物上肯定是有不小差距的。既然机器不能按照类似人类视觉的方式来寻找文章主题部分,那么机器能获取到什么呢?机器可以拿到各种标签,以及这些标签的名字、类名、位置信息,这些东西都是数据化的,于是我想到,何不把网站的css去掉看看,一个无样式的新闻页面,对于人类来说是怎样找文章的呢。结果是即使没有样式,我也能一下子找到文章主体部分。因为去掉css之后的页面有很大一个特点:不是文章的部分,大多数都是各种广告的连接。于是一个这样的想法诞生了:把那些全是连接的块去掉。

QQ新闻结构2

去掉css后很容易发现广告的特点

做到这一步之后,效果比之前那惨不忍睹的要强多了。但是去的还不够干净,因为有些广告是只有一张图的。有的新闻页下方会有一些相关评论,或者边块会有一些文字但是不是连接。于是我又得想一些办法去改进。七宝的提醒让我发现,html5里有一些语义化的标签,例如nav,head之类的,明显告诉我这一块不是文章,而在html5还没有普及的现在,有些div的类名也包含有article、content、news,这岂不是就暗示了这一块很可能是文章么。于是我豁然开朗,这样,进一步的优化方法也出来了。给定一个数组,记录所有可能是文章的类名,和很可能不是文章的类名,然后在处理页面节点的初期进行打分,抽取出最高得分的块作为文章的主体。文章的标题则直接调用了原页面的document.title。经过这一步优化,效果已经非常不错了。

这个时候我突然想起以前接触过一个叫做readability的网站,推出过一款iphone的工具,有read it later的功能,和方舟项目也比较相似。于是在网上搜了一下怎么实现,结果还真搜出一个readability的早期js源码项目,看完我要吐血了,原来自己做的工作已经有前人干过了。不过现在它已经不是基于js了。又一搜,发现有各位大牛把这个js的开源项目做成各类语言的api,其中也有php版本的,这是另外一个php版本的。看了看源码,里面有一些思想比我这个牛逼多了,比如readability会对原页面分析多次,还有诸如对标签名或者id打分则和我的一样,这样一来,我把自己的代码中readability没有的部分整合进去,并且对中文本地化抓取优化,就成了现在qiangblog上的阅读器。这里很想吐槽下国内网站标签的乱用,readability里面对视频直接整齐的用embed,但我这里要对国内众多视频的各种不同标签都要分析。

现在这个版本的算法思想大致就是这样子了。可能前面的方法说的非常简单,但实际上在最终做出成品之前加了很多优化,例如对文字段落的统计方法,视频的选取,非可视块直接删除之类,这里就不详细说明了。

总结一下,这个阅读器的优缺点。

优点:相比Safari的阅读器,这个适用的范围更广,对所有的网页都可以去尝试处理。而不像Safari,说起来Safari很“阴险”,只对处理效果好的页面才点亮阅读器的按钮,处理不来的网页压根不出现阅读器这个功能。由于直接获得文章的html代码了。如果以后想做二次处理十分方便,例如分享提取之后的文章给某人邮件。

缺点:缺点真不少,整个阅读器都是模仿模仿模仿,动画效果极力模仿,界面风格模仿,但是速度却比Safari慢,卡。不能加载下一页。由于阅读器是基于服务器处理的,在天朝伟大的G*F+W阻拦之下,难免会出现用户能访问页面,而博客服务器却拉取不到页面的尴尬问题。解决办法是放到本地js处理页面,但是会牺牲我的浏览体验,浏览器在处理文章的时候会卡主不动几秒,很不爽,所以我丢到服务端处理了。

世界,你好!

工作一年了,特别谢谢七宝给我建立博客的建议。以前一直不注重这些东西,现在想想挺重要的。
这个博客时间不长,主要想分享前端技术,记录自己工作中的经验,希望各位喜欢。